javascript - `new` 运算符如何能够覆盖硬绑定(bind),在 Function.prototype.bind(..)

标签 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()绑定(bind)到obj1,所以foo()中的this > 属于 obj1,这就是当我们调用 bar(2)obj1.a 变成 2 的原因。但是 new 运算符可以优先,并且 obj1.a 不会改变,即使 bar(3) 是用 new 调用的

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

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)函数(导致新构造的对象成为它的this),如果是这样,它会使用新创建的 this 而不是之前为此指定的硬绑定(bind)。”

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

最佳答案

首先,了解对象原型(prototype)(表示规范为 [[Prototype]] 和可通过函数 Object.getPrototypeOf 或弃用的 __proto__ 属性)和名称为 prototype 的函数的属性。每个函数都有一个名为 prototype 的属性,当使用 new 调用函数时会使用该属性。

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

{ [[Prototype]]: Foo.prototype }

让我们简要地认识一下变量的转换:

  • fToBind 是被绑定(bind)的函数:对于 foo.bind(...)foofToBind.
  • fBoundfToBind 的绑定(bind)版本;它是 bind 操作的返回值。 fBound 充当原始 fToBind 函数的看门人,并决定 thisfToBind 在被调用时获得什么.
  • oThis 是提供给 bind 的第一个参数,即绑定(bind)到函数的 this 的对象。
  • fNOP 是一个函数,其 prototype 属性设置为 fToBind.prototype
  • fBound.prototype = new fNOP() 使这些为真:

    Object.getPrototypeOf(fBound.prototype) === fNOP.prototype
    Object.getPrototypeOf(fBound.prototype) === fToBind.prototype
    

当使用 new 调用 fBound 时,提供给 fBoundthis 的形式为

{ [[Prototype]]: fBound.prototype }

fBound.prototype是一个对象的形式

{ [[Prototype]]: fNOP.prototype }

使 this 的完整形式等同于

{ [[Prototype]]: { [[Prototype]]: fNOP.prototype } }

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

The instanceof operator tests the presence of constructor.prototype in object's prototype chain.

这里&&与三元运算的顺序是:

(this instanceof fNOP && oThis) ? this : oThis

如果 this 在它的原型(prototype)链中有 fNOP.prototype 并且原始的 bind 调用被赋予一个真值第一个参数绑定(bind)到函数,然后使用 new 调用时提供给 fBound 的自然创建的 this 并将其提供给 fToBind 而不是绑定(bind)的 this

关于javascript - `new` 运算符如何能够覆盖硬绑定(bind),在 Function.prototype.bind(..),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46528091/

相关文章:

php - 使用 JavaScript 进行 RPC(远程过程调用)

javascript - 如何分析和减小 browserify 生成的文件的大小?

javascript - AngularJS Accordion is-open 检查与 ng 重复

c++ - 指针和新运算符

java - 内部类中 this.new 和 bare this 的行为

C++:新放置与自己的新重载发生冲突

php jquery 自动加载内容

javascript - 将 + 转换为复选框

c++ - operator new 如何知道内存已分配

c++ - 这个 "new"语法是什么意思?