手撕 JavaScript 代码

发布于 2021-01-26 14:13:37 字数 6318 浏览 1459 评论 0

new 的模拟实现

new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象类型之一

过程

  1. 首先创建一个新的空对象
  2. 设置原型,将对象的原型(__proto__)设置为函数的 prototype 对象
  3. 让函数的 this 指向这个对象,执行构造函数的代码(为这个新对象添加属性)
  4. 判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象

实现:

function objectFactory(){
    let Constructor = Array.prototype.shift.call(arguments)
	if(typeof Constructor !== 'function') {
        console.err('type err')
        return;
    }
    let obj = Object.create(Constructor.prototype)
    let ret = Constructor.apply(obj, arguments)
    let flag = ret && (typeof ret === "object" || typeof ret === "function")
    return flag ? ret : obj
}

Array.prototype.filter

参数:一个函数参数。这个函数接受一个默认参数,就是当前元素。这个作为参数的函数返回值为一个布尔值类型,决定元素是否保留。

filter 方法返回值为一个新的数组,这个数组里面包含参数里面所有被保留的项。

示例

let nums = [1,2,3]
let odd = nums.filter(item=>item%2) //[1,3]

实现

Array.prototype.filter = function(callback, thisArg){
    // 处理数组异常情况
    if(this === undefined || this === null) {
        throw new TypeError('this is null or not undefined')
    } 
    if(typeof callback !== 'function'){
        throw new TypeError(callback + ' is not a function')
    }
    const res = []
    // 让 O 成为回调函数的对象传递(强制转换对象)
    let O = Object(this)
    // >>>0 保证len为number, 且为正数
    let len = O.length >>> 0
    let resLen = 0
    for(let i=0; i<len; i++){
        // 检测 i 是否在O的属性中(会检查原型链)
        if(i in O){
            let element = O[i]
            if(callback.call(thisArg, O[i], i, O)){
                res[resLen++] = element
            }
        }
    }
    return res
}

关于 >>>0,请看 js中 something >>> 0是什么意思?

Array.prototype.map

  • 参数:接受两个参数,一个是回调函数,一个是回调函数的 this(可选)
  • 其中,回调函数被默认传入三个值,依次为当前元素、当前索引、整个数组。
  • 创建一个数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果
  • 对原来的数组没有影响。

实现

Array.prototype.map = function(callback, thisArg){
    if(this === null || this === undefined){
        throw new TypeError("Can not read property 'map' of null or undefined")
    }
    if(Object.prototype.toString.call(callback) !== '[object Function]'){
        throw new TypeError(callback + ' is not a function')
    }
    let res = []
    let O = Object(this)
    let len = O.length >>> 0
    for(let i=0; i<len; i++){
        if(i in O){
            //调用回调函数并传入新数组
            res[i] = callback.call(thisArg, O[i], i, this)
        }
    }
    return res
}

Array.prototype.forEach

Array.prototype.map 差不多,但没有返回值。

Array.prototype.forEach = function(callback, thisArg){
    if(this === null || this === undefined){
        throw new TypeError("Can not read property 'map' of null or undefined")
    }
    if(Object.prototype.toString.call(callback) !== '[object Function]'){
        throw new TypeError(callback + ' is not a function')
    }
    let O = Object(this)
    let len = O.length >>> 0
    for(let i=0; i<len; i++){
        if(i in O){
            callback.call(thisArg, O[i], i, this)
        }
    }
}

Array.prototype.reduce

  • 参数:一个为回调函数,一个为初始值。
  • 回调函数中三个默认参数,依次为积累值、当前值、整个数组
  • 不传默认值会自动以第一个元素为初始值,然后从第二个元素开始一次累计

示例

let nums = [1,2,3]
let newNums = nums.reduce(function(pre, cur, nums){
    return pre+cur
},0) //6

实现

Array.prototype.reduce = function(callback, initialValue){
    if(this === null || this === undefined){
        throw new TypeError("Can not read property 'map' of null or undefined")
    }
    if(Object.prototype.toString.call(callback) !== '[object Function]'){
        throw new TypeError(callback + ' is not a function')
    }
    let O = Object(this)
    let len = O.length >>> 0
    let k = 0
    let accumulator = initialValue

    // 如果第二个参数为 undefined 的情况下,数组的第一个有效值作为累加器的初始值
    if(accumulator === undefined){
        while(k<len && !(k in O)){
            k++
        }
        if(k >= len){
            throw new TypeError('Reduce of empty array with no initial value')
        }
        accumulator = O[k++]
    }
    while(k < len){
        if(k in O){
            accumulator = callback.call(undefined, accumulator, O[k], O)
        }
    }
    return accumulator
}

call 的模拟实现

call() 方法在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法。

Function.prototype.call = function(context = window, ...args){
    if(typeof this !== 'function'){
        throw new TypeError('Type Error')
    }
    context._fn = this
    let result = context._fn(...args)
    delete context._fn
    return result
}

apply 的模拟实现

第一个参数为绑定的this,默认为window,第二个参数是数组或类数组

Function.prototype.apply = function(context = window, args){
    if(typeof this !== 'function'){
        throw new TypeError('Type Error')
    }

    context._fn = this
    let result = context._fn(...args)
    delete context._fn
    return result
}

bind 的模拟实现

bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。

【注意】:一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。

Function.prototype.bind = function(context = window, ...args){
    if(typeof this !== "function"){
        throw new Error("Type Error")
    }
    // 保存 this 的值
    var self = this

    return function F(){
        //考虑new的情况
        // 当作为构造函数时,this 指向实例,此时结果为 true,将绑定函数的 this 指向该实例,可以让实例获得来自绑定函数的值
        if(this instanceof F){
            return new self(...args, ...arguments)
        }
        return self.apply(context, [...args, ...arguments])
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

JSmiles

生命进入颠沛而奔忙的本质状态,并将以不断告别和相遇的陈旧方式继续下去。

0 文章
0 评论
84961 人气
更多

推荐作者

浪漫人生路

文章 0 评论 0

620vip

文章 0 评论 0

羞稚

文章 0 评论 0

走过海棠暮

文章 0 评论 0

你好刘可爱

文章 0 评论 0

陌若浮生

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文