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

时间:2017-10-02 14:55:19

标签: javascript new-operator

这是一个纯粹的理论问题。 我正在学习“你不知道js”的javascript,而且我一直坚持JS中bind函数的实现。请考虑以下代码:

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

在上面的代码段中,我们将foo()绑定到obj1,因此this中的foo()属于obj1,这就是为什么obj1.a当我们致电2时,会变为bar(2)。但是new运算符可以优先,obj1.a即使用bar(3)调用new也不会更改。

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

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;
};
}

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

this instanceof fNOP &&
oThis ? this : oThis

// ... and:

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

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

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

3 个答案:

答案 0 :(得分:8)

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

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

this

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

  • { [[Prototype]]: Foo.prototype } 是受约束的函数:对于fToBindfoo.bind(...)foo
  • fToBindfBound的绑定版本;它是fToBind操作的返回值。 bind充当原始fBound函数的守门员,并决定调用fToBindthis时获取的内容。
  • fToBind是提供给oThis的第一个参数,即绑定到函数bind的对象。
  • thisfNOP属性设置为prototype的函数
  • fToBind.prototype导致这些是真的:

    fBound.prototype = new fNOP()

使用Object.getPrototypeOf(fBound.prototype) === fNOP.prototype Object.getPrototypeOf(fBound.prototype) === fToBind.prototype 调用fBound时,提供给new的{​​{1}}格式为

this

fBound

形式的对象
{ [[Prototype]]: fBound.prototype }

使fBound.prototype的完整形式等同于

{ [[Prototype]]: fNOP.prototype }

因此,当使用this调用{ [[Prototype]]: { [[Prototype]]: fNOP.prototype } } 时,fNOP.prototype位于新创建的this对象的原型链中。这正是fBound操作测试的内容:

  

new运算符测试object instanceof constructor原型链中instanceof的存在。

此处constructor.prototype和三元之间的操作顺序为:

object

如果&&在其原型链中有(this instanceof fNOP && oThis) ? this : oThis ,则原始this调用会被赋予一个真正的第一个参数来绑定到该函数,然后使用当使用fNOP.prototype调用bind并将其提供给this而不是绑定的fBound时,自然创建的new已提供给fToBind

答案 1 :(得分:1)

new优先于绑定的this值,因为这是语言的定义方式。

首先你有一个功能。然后绑定this值,并正常调用它。正如所料,this的值是绑定值。

然后用new调用相同的函数,并覆盖您的值。为什么?因为语言设计指示使用new的调用,因此通过语言实现,忽略绑定的this值,并将其替换为正在构造的新对象。

语言实现只是一个程序。和任何其他程序一样,它遵循规则。因此,在这种情况下的规则是new可以指示this的值,而不管任何约束值。

答案 2 :(得分:1)

我想你只是问他们如何让polyfill工作。

在该polyfill中,fNOP是一个无操作函数(在调用时不执行任何操作),仅用于将.prototype插入到返回的fBound函数的原型链中。这就完成了:

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

然后,当fBound函数(返回到.bind()的调用者的函数)被调用时,instanceof运算符可以检查this内的fBound值。 fNOP函数用于查看该值是否为new的实例。如果是,则推断使用了instanceof

这是有效的(因为.prototype将从它左侧给出的对象开始,并搜索原型链以查看它们中的任何一个是否与{ {1}}右侧的函数对象。因此,如果调用了newthis值将是一个新对象,其原型链中有fNOP.prototype,因为上面已执行了设置。

然而,这不是一个完美的测试方法。例如,可以使用.call()方法将调用的this值设置为fBound函数的某个其他实例。因此,即使它不是new,它也会被使用,因此this的绑定值不会被用作调用的this值原来的功能。