1.首先先明確,this會出現在哪里。
this出現在全局作用域中,或函數作用域中(普通函數、箭頭函數)。
對象是不產生作用域的,對象的{}和函數的{}不一樣,this并不會直接出現在對象或類中,只會出現在對象/類的函數中。
2.this指向哪里
this在全局作用域下指向的是window。
this在函數中指向的比較復雜, 滿足以下四點:
1.函數在調用時,JavaScript會默認給this綁定一個值;
2.this的綁定和定義的位置(編寫的位置)沒有關系;
3.this的綁定和調用方式以及調用的位置有關系;
4.this是在運行時被綁定的;
3.this的綁定規則
以下指的規則都是普通函數中的,不包括箭頭函數。
1.默認綁定
如果一個函數調用是直接調用,比如fn()這樣,沒有對象.fn(),就說它是默認綁定的,通常默認綁定時,函數中的this指向全局對象(window);
且要找到函數真正的調用位置,看它是否是獨立函數直接調用。
function foo() {console.log(this); // window
}foo();
2.隱式綁定
通過某個對象進行調用的。比如對象.fn()。通過某個對象發起的函數調用。這時候this會被綁定在該對象身上
function foo() {console.log(this); // obj對象
}var obj = {name: "why",foo: foo
}obj.foo();
上面提到要找到函數真正的調用位置,看它是否是獨立函數直接調用。
下面舉個例子,這個實際的調用位置其實是直接調用的。
function foo() {console.log(this);
}var obj1 = {name: "obj1",foo: foo
}// 講obj1的foo賦值給bar
var bar = obj1.foo;
bar();
3.顯式綁定
通過apply、call、bind進行綁定就是顯式綁定
function foo() {console.log(this);
}foo.call(window); // window
foo.call({name: "why"}); // {name: "why"}
foo.call(123); // Number對象,存放時123
4.new綁定
使用new,這個新對象會綁定到函數調用的this上。
綁定的this是這個類/構造函數的實例對象。
// 創建Person
function Person(name) {console.log(this); // Person {}this.name = name; // Person {name: "why"}
}var p = new Person("why");
console.log(p);
4.特殊的this
1.setTimeout
setTimeout內部如果傳的是普通函數,那這個this指向的是window。
這和setTimeout源碼的內部調用有關,記住就好了。
setTimeout(function() {console.log(this); // window
}, 1000);
2.forEach
內部傳普通函數,this指向的也是window。
var names = ["abc", "cba", "nba"];
names.forEach(function(item) {console.log(this); // 三次window
});
3.dom節點
this指向的是綁定事件的節點。
var box = document.querySelector(".box");
box.onclick = function() {console.log(this); // box對象
}
4.箭頭函數
箭頭函數沒有this,也就是不綁定this,如果在箭頭函數中使用this,則是根據外層作用域來決定this。
所以與上面的setTimeout、forEach特點結合,經常在setTimeout、forEach中使用箭頭函數。
var obj = {data: [],getData: function() {setTimeout(() => {// 模擬獲取到的數據var res = ["abc", "cba", "nba"];this.data.push(...res);}, 1000);}
}obj.getData();
這里,setTimeout中通過箭頭函數調用this,這個this與外層作用域相同,也就是和在getData函數中使用this的指向相同。
但如果getData也是個箭頭函數,那么getData函數中使用this的指向 與 外層作用域相同,也是與全局作用域中的this一致,window。
var obj = {data: [],getData: () => {setTimeout(() => {console.log(this); // window}, 1000);}
}obj.getData();
5.規則優先級
new綁定 > 顯式綁定(bind)> 隱式綁定 > 默認綁定
1.new綁定和call、apply是不允許同時使用的,所以不存在誰的優先級更高
但new綁定優先級高于bind
function foo() {console.log(this);
}var obj = {name: "obj"
}// var foo = new foo.call(obj);
var bar = foo.bind(obj);
var foo = new bar(); // 打印foo, 說明使用的是new綁定
2.new綁定優先級高于隱式綁定
function foo() {console.log(this);
}var obj = {name: "why",foo: foo
}new obj.foo(); // foo對象, 說明new綁定優先級更高
3.顯式綁定優先級高于隱式綁定
function foo() {console.log(this);
}var obj1 = {name: "obj1",foo: foo
}var obj2 = {name: "obj2",foo: foo
}// 隱式綁定
obj1.foo(); // obj1
obj2.foo(); // obj2// 隱式綁定和顯式綁定同時存在
obj1.foo.call(obj2); // obj2, 說明顯式綁定優先級更高
參考:前端面試之徹底搞懂this指向