js 实现call, apply, bind方法

123 阅读2分钟

这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战

第一层 - 绑定在原型上的方法

得益于 JS 原型链的特性。由于 function xxx 的原型链 指向的是 Function.prototype , 因此我们在调用 xxx.bind 的时候,调用的是 Function.prototype 上的方法。

Function.prototype._bind = function() {}

第二层 - 改变 this 的指向

bind 最核心的特性 - 改变 this 的指向+返回一个函数。
可以通过已知的 apply 和 call 来实现,这里我们就暂且使用 apply 来进行模拟。首先通过 self 来保存当前 this,也就是传入的函数。因为this 具有 隐式绑定的规则(

Function.prototype._bind = function(thisObj) {
    const self = this;
    return function () { 
        self.apply(thisObj); 
    }
}

第三层 - 支持柯里化

Function.prototype._bind = function(thisObj) {
    const self = this;
    const args = [...arguments].slice(1)
    return function () {
        const finalArgs = [...args, ...arguments] 
        self.apply(thisObj, finalArgs); 
    }
}

第四层 - 考虑 new 的调用

通过 bind 绑定之后,依然是可以通过 new 来进行实例化的, new 的优先级会高于 bind

因此需要自己实现一个 new ,

new关键字会进行如下的操作: 1.创建一个空的简单JavaScript对象(即{}); 2.链接该对象(设置该对象的constructor)到另一个对象 ; 3.将步骤1新创建的对象作为this的上下文 ; 4.如果该函数没有返回对象,则返回this。

Function.prototype._bind = function(thisObj) {
    const self = this;
    const args = [...arguments].slice(1);
    return function () {
        const finalArgs = [...args, ...arguments];
        // new.target 用来检测是否是被 new 调用
        if(new.target !== undefined) {
            // this 指向的为构造函数本身
            var result = self.apply(this, finalArgs);
            // 判断该函数是否返回对象
                if(result instanceof Object) {
                    return reuslt; 
                }
                // 没有返回对象就返回 this
                return this; 
            } else {
                // 如果不是 new 就原来的逻辑
                return self.apply(thisArg, finalArgs); 
        } 
    }
}

第五层 - 保留函数原型

最后需要给 prototype 补上 - 调用对象必须为函数。

Function.prototype._bind = function (thisObj) {
    // 判断是否为函数调用
    if (typeof target !== 'function' || Object.prototype.toString.call(target) !== '[object Function]') {
        throw newTypeError(this + ' must be a function'); 
    }
    const self = this;
    const args = [...arguments].slice(1);
    var bound = function () {
        var finalArgs = [...args, ...arguments];
        // new.target 用来检测是否是被 new 调用
        if (new.target !== undefined) {
        // 说明是用new来调用的
            var result = self.apply(this, finalArgs);
            if (result instanceofObject) {
                return result; 
            }
        return this; 
    }
    else {
        return self.apply(thisArg, finalArgs); 
        }
    };
    if (self.prototype) {
        // 为什么使用了 Object.create? 因为我们要防止,bound.prototype 的修改而导致self.prototype 被修改。不要写成 bound.prototype = self.prototype; 这样可能会导致原函数的原型被修改。 
        bound.prototype = Object.create(self.prototype); 
        bound.prototype.constructor = self;
    }
    return bound;
};