我正在阅读You don't know JS。在其中,有一节讨论soft binding技术。基本上它是将函数绑定到特定范围/上下文的变体。
从书中可以看出:
如果有一种方法可以为默认绑定提供不同的默认值(不是全局或未定义),那么这仍然很好,同时仍然可以通过隐式绑定或显式绑定技术手动绑定该函数。
if (!Function.prototype.softBind) {
Function.prototype.softBind = function(obj) {
var fn = this,
curried = [].slice.call( arguments, 1 ),
bound = function bound() {
return fn.apply(
(!this ||
(typeof window !== "undefined" &&
this === window) ||
(typeof global !== "undefined" &&
this === global)
) ? obj : this,
curried.concat.apply( curried, arguments )
);
};
bound.prototype = Object.create( fn.prototype );
return bound;
};
}
一般来说,我理解除了这部分之外该功能的作用:
bound.prototype = Object.create( fn.prototype );
为什么在使用这种“软绑定”技术时我们必须设置原型?
答案 0 :(得分:2)
为什么我们必须在使用这个"软绑定时设置原型"技术
完整性比其他任何事情更重要。如果有人试图创建软绑定函数的new
实例,他们希望生成的对象[[Prototype]]
链接到与原始函数相同的对象{{1指出。因此,我们确保将.prototype
设置为等于引用相同的对象。
有人想要在软约束函数上调用bound.prototype
的可能性很低,我想,但为了更安全,它仍然包括在内。同样,顺便说一句,对于内置new
的polyfill也是如此:
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind#Polyfill
答案 1 :(得分:0)
来自that页面:
为什么new能够覆盖硬绑定有用?
此行为的主要原因是创建一个函数(可以 用于构造对象的新东西,基本上忽略了 这个硬绑定但预设部分或全部功能 参数。 bind(..)的一个功能是任何参数 在第一个此绑定参数默认为标准后传递 底层函数的参数(技术上称为“部分函数”) 应用程序“,这是”currying“的一个子集。)
我猜想softbind具有相同的好处(提供一个传递给它的默认参数的构造函数)你必须设置原型,或者当你使用绑定函数作为构造函数时你缺少原型函数:
//softBind code removed
function Test(arg1,arg2){
this.arg1=arg1;
this.arg2=arg2;
}
Test.prototype.sayArgs=function(){
console.log('args:',this.arg1,this.arg2);
}
//the binding part doesn't do anything so can pass null
var BoundTest = Test.softBind(null, 'arg1');
var bt = new BoundTest('arg2');
bt.sayArgs();//=args:arg1 arg2
如果您打算以这种方式使用它,虽然您不必将softBind添加到Function.prototype,您可以使用普通绑定,本机绑定或MDN polyfill。
请注意,使用polyfill时,您无法将falsy参数作为第一个要绑定的参数传递,否则它将会中断。
//Using the MDN bind polyfill you can't pass any falsy
// value like null, undefined,"",0 and false
var BoundTest = Test.bind({}, 'arg1');
var bt = new BoundTest('arg2');
bt.sayArgs();//=args:arg1 arg2;
在文章中,给出的例子实际上是破碎的:
var bar = foo.bind( null, "p1" );
var baz = new bar( "p2" );
应该是:
var bar = foo.bind('anything non falsy' , "p1" );
<强> [更新] 强>
//removed if, so it'll set softbind every time
Function.prototype.softBind = function(obj) {
var fn = this,
curried = [].slice.call( arguments, 1 ),
bound = function bound() {
return fn.apply(
(!this ||
(typeof window !== "undefined" &&
this === window) ||
(typeof global !== "undefined" &&
this === global)
) ? obj : this,
curried.concat.apply( curried, arguments )
);
};
bound.prototype = Object.create( fn.prototype );
return bound;
};
function Test(){
//this has the right prototype and has
// the default arguments
console.log('this is:', this,'arguments',arguments);
this.name='test';
}
Test.prototype.something=22
var BoundTest = Test.softBind(null,'arg1','arg2');
var bt = new BoundTest();
//bt is same as new Test('arg1','arg2') would be
console.log('bt is:',bt);
//this.name did not affect window
console.log(window.name);//not test
console.log(bt instanceof Test);//true