接上篇文章JavaScript重识bind、call、apply缓存
function foo(something) {
this.a = something;
}
var obj1 = {};
var bar = foo.bind( obj1 );
bar( 2 ); // obj1.a === 2
var baz = new bar(3);
console.log( obj1.a ); // 2
console.log( baz.a ); // 3
复制代码
bar
被硬绑定到 obj1
上,可是 new bar(3)
没有将obj1.a
修改成 3。相反,new
修改了硬绑定(到 obj1
的)调用 bar(..)
中的 this
。由于使用了 new
绑定,咱们获得了一个名字为 baz
的新对象,而且 baz.a
的值是 3。bash
if (!Function.prototype.bindNew) {
Function.prototype.bindNew = function(oThis) {
//一个函数去调用,也就是说bind,call,apply的this是个函数;
//而后再去改变这个函数里面的this;
if (typeof this !== "function") {
// 与 ECMAScript 5 最接近的
// 内部 IsCallable 函数
throw new TypeError(
"Function.prototype.bind - what is trying " +
"to be bound is not callable"
);
}
//这里将初始化的参数缓存起来;
var aArgs = Array.prototype.slice.call( arguments, 1 ),
// ftoBind 指向要bind的函数;
fToBind = this,
// 返回一个新函数
fNOP = function(){},
fBound = function(){
//fToBind.apply 改变绑定this;
// 执行的时候判断,当前this等于fNOP而且传入oThis,就设置成当前this,否则就改变成初始化传入的oThis;
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;
};
}
复制代码
(后面会介绍为何要在 new 中使用硬绑定函 数)app
下面是 new 修改 this 的相关代码:函数
this instanceof fNOP &&
oThis ? this : oThis ;
// ... 以及:
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
复制代码
这段代码会判断硬绑定函数是不是被 new 调用,若是是的话就会使用新建立 的 this 替换硬绑定的 this。 若是你这样子调用:post
function foo() {
console.log("name: " + this.name);
}
var obj = { name: "obj" };
var obj2 = { name: "obj2" }, obj3 = { name: "obj3" };
var dd = foo.bindNew(obj2);
var dj = new dd();// name:undefined; 而不是name:obj2
复制代码
由于new操做修改了this的指向;this绑定的就是是新建立的对象-dj。 详细解释一下:ui
// ftoBind 指向要bind的函数; 这里是foo;
fToBind = this,
// 返回一个新函数
fNOP = function(){},
fBound = function(){
//fToBind.apply 改变绑定this;
// 执行的时候判断,当前this等于fNOP而且传入oThis,就设置成当前this,否则就改变成初始化传入的oThis;
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
// fNOP的原型指向this的原型,this此时指向foo;
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
复制代码
这个代码使 fBound 为 fNOP 的实例;spa
return fToBind.apply(
(this instanceof fNOP && oThis ? this : oThis ),
aArgs.concat(Array.prototype.slice.call( arguments ) )
复制代码
此时的
fToBind
,是以前执行bindNew
指向的foo
;prototype
此时
this
,就是指向的new dd()
后返回的新实例;this instanceof fNOP === true
code
(this instanceof fNOP && oThis ? this : oThis )
这个就返回this
; 那么这个新对象上面是没有obj
这个属性的,foo.apply
,执行foo
后,就打印出name:undefined
;
上面是手写bind而后来剖析bind内部的绑定机制;那么咱们实际检测也会等到一样的结果; 就是本文最开始的代码:
function foo(something) {
this.a = something;
}
var obj1 = {};
var bar = foo.bind( obj1 );
bar( 2 ); // obj1.a === 2
var baz = new bar(3);
console.log( obj1.a ); // 2
console.log( baz.a ); // 3
复制代码
这样就明白了为何baz.a
是3,而不是2了,由于new
后,改变了bar
的this
指向;使其新new
的实例 baz
; foo
的this.a = something;
就将 baz.a = 3
了; 这里也能够得出结论,new
操做改变this
绑定的优先级高于硬绑定(bind,apply,call
);
若是 new 中使用硬绑定函数,就能够预先设置函数的一些参数,这样在使用 new 进行初始化时就能够只传入其他的参数。bind(..) 的功能之一就是能够把除了第一个 参数(第一个参数用于绑定 this)以外的其余参数都传给下层的函数(这种技术称为“部 分应用”,是“柯里化”的一种)。举例来讲:
function foo(p1,p2) {
this.val = p1 + p2;
}
// 之因此使用 null 是由于在本例中咱们并不关心硬绑定的 this 是什么
// 反正使用 new 时 this 会被修改
var bar = foo.bind( null, "p1" );
var baz = new bar( "p2" );
baz.val; // p1p2
复制代码