如何`new`操作符能够在Function.prototype.bind(..)中覆盖硬绑定

vik*_*ant 14 javascript new-operator

这是一个纯粹的理论问题.我从'你不知道js'学习javascript,我被困bind在JS 中的函数实现.考虑以下代码:

function foo(something) {
  this.a = something;
}

var obj1 = {};

var bar = foo.bind(obj1);
bar(2);
console.log(obj1.a); // 2

var baz = new bar(3);
console.log(obj1.a); // 2
console.log(baz.a); // 3
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,我们绑定foo()obj1,所以thisfoo()属于obj1这就是为什么obj1.a变成2当我们调用bar(2).但是new操作员能够优先使用,obj1.a即使bar(3)被调用也不会改变new.

以下是MDN页面提供的polyfill bind(..):

if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
    if (typeof this !== "function") {
        // closest thing possible to the ECMAScript 5
        // internal IsCallable function
        throw new TypeError( "Function.prototype.bind - what " +
            "is trying to be bound is not callable"
        );
    }

    var aArgs = Array.prototype.slice.call( arguments, 1 ),
        fToBind = this,
        fNOP = function(){},
        fBound = function(){
            return fToBind.apply(
                (
                    this instanceof fNOP &&
                    oThis ? this : oThis
                ),
                aArgs.concat( Array.prototype.slice.call( arguments ) )
            );
        }
    ;

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();

    return fBound;
};
}
Run Code Online (Sandbox Code Playgroud)

根据这本书允许新覆盖的部分是:

this instanceof fNOP &&
oThis ? this : oThis

// ... and:

fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
Run Code Online (Sandbox Code Playgroud)

所以,现在的重点.根据这本书:"我们实际上不会深入解释这种技巧是如何工作的(它复杂并且超出了我们的范围),但实际上该实用程序确定是否使用new调用了硬绑定函数(导致新构造的对象就是它的这个),如果是这样的话,它会使用新创建的对象,而不是先前为此指定的硬绑定."

bind()函数中的逻辑如何允许new操作符覆盖硬绑定?

aps*_*ers 8

首先,了解对象的原型([[Prototype]]通过函数Object.getPrototypeOf或不推荐的__proto__属性表示规范和可访问的)与名称为的函数的属性之间的区别非常重要prototype.每个函数都有一个名为的属性prototype,在调用函数时使用new.

当您调用函数时new,该函数提供的this值设置为新构造的对象,其原型(即[[Prototype]])设置为prototype被调用函数的属性.也就是说,当你调用时new Foo(),那么当Foo运行代码时,该this值将是表单的对象

{ [[Prototype]]: Foo.prototype }
Run Code Online (Sandbox Code Playgroud)

让我们简要地讨论一下变量:

  • fToBind是绑定的函数:for foo.bind(...),foois fToBind.
  • fBound是绑定版本fToBind; 它是操作的返回值bind.fBound充当原始fToBind函数的守门员,并决定调用它时获得的thisfToBind.
  • oThis是提供给的第一个参数bind,即绑定到函数的对象this.
  • fNOP是一个prototype属性设置为的函数fToBind.prototype
  • fBound.prototype = new fNOP() 导致这些是真的:

    Object.getPrototypeOf(fBound.prototype) === fNOP.prototype
    Object.getPrototypeOf(fBound.prototype) === fToBind.prototype
    
    Run Code Online (Sandbox Code Playgroud)

fBound调用时new,则this提供给fBound表单的那个

{ [[Prototype]]: fBound.prototype }
Run Code Online (Sandbox Code Playgroud)

并且fBound.prototype是表单的对象

{ [[Prototype]]: fNOP.prototype }
Run Code Online (Sandbox Code Playgroud)

制作this相当于的完整形式

{ [[Prototype]]: { [[Prototype]]: fNOP.prototype } }
Run Code Online (Sandbox Code Playgroud)

因此,fNOP.prototype在调用this时,是在新创建的对象的原型链中.这正是操作测试的内容:fBoundnewobject instanceof constructor

所述instanceof操作者测试的存在constructor.prototypeobject的原型链.

&&这里和三元之间的操作顺序是:

(this instanceof fNOP && oThis) ? this : oThis
Run Code Online (Sandbox Code Playgroud)

如果this具有fNOP.prototype在其原型链原始bind呼叫被赋予结合的功能的truthy第一个参数,然后使用自然地创建this提供给fBound当它被调用new并提供该到fToBind的,而不是结合this.