接上篇文章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
複製代碼