4.call,apply,bind函数的实现

154 阅读3分钟

1.call函数实现

call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

思路:

  • 根据call的规则设置上下文对象,也就是this的指向,

  • 将函数的this指向隐式绑定到context上,

  • 通过隐式绑定执行函数并传递参数。

  • 删除临时属性,返回函数执行结果


Function.prototype.myCall = function (context, ...arr) {
    // 指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中为window)
    (context === null || context === undefined) ?  window : Object(context);
    const specialPrototype = Symbol('特殊属性Symbol'); // 用于临时储存函数
    context[specialPrototype] = this; // 函数的this指向隐式绑定到context上
    let result = context[specialPrototype](...arr); // 通过隐式绑定执行函数并传递参数
    delete context[specialPrototype]; // 删除上下文对象的属性
    return result; // 返回函数执行结果
};

2.apply函数实现

apply() 方法调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数。

思路:

  • call是apply的语法糖,传递参数方式不一样,对其参数进行处理

Function.prototype.myApply = function (context) {
    (context === null || context === undefined) ?  window : Object(context);
    // JavaScript权威指南判断是否为类数组对象
    function isArrayLike(o) {
        if (o &&                                    // o不是null、undefined等
            typeof o === 'object' &&                // o是对象
            isFinite(o.length) &&                   // o.length是有限数值
            o.length >= 0 &&                        // o.length为非负值
            o.length === Math.floor(o.length) &&    // o.length是整数
            o.length < 4294967296)                  // o.length < 2^32
            return true
        else
            return false
    }
    const specialPrototype = Symbol("特殊属性Symbol")
    context[specialPrototype] = this; // 隐式绑定this指向到context上
    let args = arguments[1]; // 获取参数数组
    let result
    // 处理传进来的第二个参数
    if (args) {
        // 是否传递第二个参数
        if (!Array.isArray(args) && !isArrayLike(args)) {
            throw new TypeError('myApply 第二个参数不为数组并且不为类数组对象抛出错误');
        } else {
            args = Array.from(args) // 转为数组
            result = context[specialPrototype](...args); // 执行函数并展开数组,传递函数参数
        }
    } else {
        result = context[specialPrototype](); // 执行函数
    }
    delete context[specialPrototype]; // 删除上下文对象的属性
    return result; // 返回函数执行结果
};

3.bind函数实现

bind()方法创建一个新的函数,在bind()被调用时,这个新函数的this被bind的第一个参数指定,其余的参数将作为新函数的参数供调用时使用。

思路:

  • 拷贝源函数
  • 构造一个中间函数,其内通过instanceof判断函数是否通过new调用,来决定绑定的context
  • 在这中间函数中用apply调用源函数,并且传入两次所传递的参数
  • 将源函数的prototype链接给中间函
  • 返回这个中间函数

Function.prototype.myBind = function (objThis, ...params) {
    const thisFn = this; // 存储源函数以及上方的params(函数参数),thisFn=>被绑定的函数
    // 对返回的函数 secondParams 二次传参
    let fToBind = function (...secondParams) {
        const isNew = this instanceof fToBind // this是否是fToBind的实例 
        // new调用就绑定到this上,否则就绑定到传入的objThis上
        const context = isNew ? this : Object(objThis) 
         // 用apply调用源函数绑定this的指向并传递参数,返回执行结果
        return thisFn.call(context, ...params,...secondParams);
    };
    fToBind.prototype = Object.create(thisFn.prototype); // 复制源函数的prototype给fToBind
    return fToBind; // 返回拷贝的函数
};

参考来源:

MDN

js基础-面试官想知道你有多理解call,apply,bind?[不看后悔系列]