JavaScript中有四條調用規則,我們可以根據這四條調用規則來判斷當前this的指向。
第一條:默認綁定。
function foo(){
console.log(this.a);
}
var a = 2;
foo(); // 2
代碼很簡單,那要怎麼給默認綁定一個定義呢?答案就是:不帶任何修飾的函數引用進行調用。這個時候this指向window.(嚴格模式下指向undefined)
第二條:隱式綁定
隱式綁定,需要在一個對象內部包含一個指向函數的屬性,並通過這個屬性間接引用函數,從而把this間接綁定到這個對象上。
function foo(){
console.log(this.a);
}
var obj = {
a:2,
foo:foo
}
obj.foo(); // 2
這裏在調用的時候增加了一個上下文對象,隱式綁定會把函數調用中的this綁定到這個上下文對象。如果存在多層,按照最近的一層合賬。a.b.c.foo(); 則實際指向的是c.在這裏呢,有一種特殊的情況,函數的賦值操作。
function foo(){
console.log(this.a);
}
function lee(func){
func();
}
var obj = {
a:2,
foo:foo
}
var a = 1;
doFoo(obj.foo); //1
看似是隱式綁定,但是函數在傳遞參數的時候就執行了賦值操作,這個時候再次調用的綁定規則應該爲默認綁定,所以指向window.第三條:顯式綁定
這裏呢,關於call和apply以及bind的知識,之前有說過,不清楚的可以先了解一下。這些函數都可以直接指定this的指向,所以是顯式綁定。call和apply 仍然可以通過調用時傳遞的參數去修改當前this的綁定,現在想象一個場景:在一個函數內部要調用一個函數,需要將這個函數綁定在一個obj對象上,但是會有其他因素影響函數綁定到obj對象上,這樣一來就會產生丟失綁定的問題。這個時候可以使用硬綁定:
function foo(){
console.log(this.a);
}
var obj = {
a:2,
}
var bar = function(){
foo.call(obj);
}
bar(); //2
setTimeout(bar,100); //2
bar.call(window); //2
這樣一來,就無法修改函數的綁定對象了。ES5提供了內置方法,Function.prototype.bind方法,每個function都自帶bind方法,
var bar = foo.bind(obj)
如此就可以使用了。第四條:new綁定
也就是通過new去實例化一個對象。、function foo(a){
this.a = a;
}
var bar = new foo(2);
console.log(bar.a); //2
一言以蔽之:構造一個新對象,並把它綁定到foo()調用中的this上。多說一句:這裏foo()是一個普通的函數,通過new關鍵字調用就是對函數的構造調用。由此可得出JS中實際上並不存在所謂的構造函數,只有對函數的構造調用。
優先級:
說了四種方式,既然存在那難免就會有個比較,到底誰更優先呢?function foo(value){
this.a = value;
}
var obj1 = {
foo:foo
}
var obj2 = {}
obj1.foo(2);
console.log(obj1.a); //2
obj1.foo.call(obj2,3);
console.log(obj2.a); //3 顯式 > 隱式
--------------------------------------------
var oo1 = {};
var bar = foo.bind(oo1);
bar(2); //001.a = 2
var oo2 = new bar(3);
// oo1.a = 2,oo2.a = 3; => new > 顯式
之前我們看過的硬綁定的實現方式,按道理來說應該是無法修改纔對的,而實際使用的時候bind()有自己的實現機制,所以new調用仍然會修改當前指向對象。這裏是傳統的四種綁定方式,有沒有例外呢,有。感興趣的可以看下我總結的箭頭函數對this的影響,