这是一个纯粹的理论问题。
我正在学习“你不知道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
运算符覆盖硬绑定?
答案 0 :(得分:8)
首先,了解对象的原型(表示规范为[[Prototype]]
并通过函数Object.getPrototypeOf
或已弃用的__proto__
属性可访问)与属性之间的区别非常重要。名为prototype
的函数。每个函数都有一个名为prototype
的属性,在使用new
调用函数时使用该属性。
当您使用new
调用函数时,该函数将提供this
值设置为新构造的对象,其原型(即[[Prototype]]
)设置为{ {1}}被调用函数的属性。也就是说,当您调用prototype
时,当new Foo()
内的代码运行时,Foo
值将成为表单的对象
this
让我们简要地讨论一下变量:
{ [[Prototype]]: Foo.prototype }
是受约束的函数:对于fToBind
,foo.bind(...)
为foo
。fToBind
是fBound
的绑定版本;它是fToBind
操作的返回值。 bind
充当原始fBound
函数的守门员,并决定调用fToBind
值this
时获取的内容。fToBind
是提供给oThis
的第一个参数,即绑定到函数bind
的对象。this
是fNOP
属性设置为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}}右侧的函数对象。因此,如果调用了new
,this
值将是一个新对象,其原型链中有fNOP.prototype
,因为上面已执行了设置。
然而,这不是一个完美的测试方法。例如,可以使用.call()
方法将调用的this
值设置为fBound
函数的某个其他实例。因此,即使它不是new
,它也会被使用,因此this
的绑定值不会被用作调用的this
值原来的功能。