原文鏈接: 掘金-this、apply、call、bind
一、有關this綁定的案例
this永遠指向最後調用它的那個對象
Demo1:
var name = "windowName";
function a(){
var name = "innerName";
console.log(this.name); // 在非嚴格模式下(瀏覽器環境下)結果爲windowName,在嚴格模式下(Node環境下)結果爲undefined
console.log(this); // 在非嚴格模式下(瀏覽器環境下)結果爲window對象,在嚴格模式下(Node環境下)報錯
}
a();
console.log(this.name); // 在非嚴格模式下(瀏覽器環境下)結果爲windowName,在嚴格模式下(Node環境下)結果爲{}
Demo2:
var name = "windowName";
var obj = {
name: 'innerName',
fn: function () {
console.log(this.name); // 在非嚴格和嚴格模式下結果都是innerName
}
}
obj.fn(); // 注: 這次調用函數的是obj對象
// 補充一點,假如把obj中的name換成name1:
var obj1 = {
name: 'innerName',
fn: function () {
console.log(this.name); //結果就是undefined
}
}
Demo3: 調用時將obj換成window.obj, 由於最後調用的對象依然是obj,所以打印的結果仍然是innerName
Demo4:
// 把obj中的name註釋掉
var name = "windowName";
var obj = {
// name: 'innerName',
fn: function () {
console.log(this.name); // undefined
}
}
obj.fn();
fn的最後一個調用對象是obj,而obj中此時並沒有name屬性,也不會向上一個對象中查找,所以輸出結果爲undefined
Demo5:
var name = "windowName";
var obj = {
name: null,
fn: function () {
console.log(this.name); // 非嚴格模式下結果爲windowName
}
}
var f = obj.fn;
f();
本例中最關鍵的一處在於obj.fn的指向給了f,所以此時調用fn函數的對象並不是obj,而是window,所以輸出結果爲windowName
Demo6:
var name = "windowName";
function fn(){
var name = "innerName";
function innerFn(){
console.log(this.name); // windowName
}
innerFn();
}
fn();
我們會以爲innerFn在fn裏面調用this會指向fn, 但是最先執行的函數fn它的指向對象是window,所以innerFn中this的指向也是window,所以打印結果爲innerName
二、如何修改this指向
- 使用ES6箭頭函數
- 在函數內部使用 var that = this;
- 使用apply、call、bind
- new實例化一個對象
思考下面的代碼:
var name = "windowName";
var obj = {
name: "innerName",
func1: function(){
console.log(this.name);
},
func2: function(){
setTimeout(function(){
this.func1();
},100)
}
}
obj.func2();
答案是:
解釋: 雖然func2的調用對象是obj沒錯,但是func2中的setTimeout函數的調用對象其實是window對象,this指向的也就是window對象。 window對象中並沒有func1函數,所以就報錯了。。
2.1 箭頭函數改造
箭頭函數謹記: 箭頭函數的this始終指向函數定義時的this,而非函數執行時。箭頭函數中沒有this綁定,必須通過查找作用域鏈來決定其值。如果箭頭函數被非箭頭函數包含,this綁定的時最近一層非箭頭函數的this,否則this爲undefined
改造後的代碼:
var name = "windowName";
var obj = {
name: "innerName",
func1: function(){
console.log(this.name);
},
func2: function(){
setTimeout( ()=> {
this.fun1(); // innerName
},100)
}
}
obj.func2();
2.2 使用var that = this;保存this指向
var name = "windowName";
var obj = {
name: "innerName",
func1: function(){
console.log(this.name);
},
func2: function(){
var that = this;
setTimeout(function(){
that.fun1(); // innerName
},100)
}
}
obj.func2();
func2中this的指向時調用func2方法的obj, 在調用setTimeout函數之前,我們先將this賦值給that,那麼that就指向了obj,在setTimeout函數中that也是指向obj。
2.3 使用apply、call、bind修改this指向
2.3.1 使用apply 原先setTimeout函數中的this是指向window,我們需要將其指向obj,在其函數後面使用.apply(obj)即可改變this指向
2.3.2 call 和 bind 用法與上面相同。
面試考點1: apply和call有什麼區別
答: apply和call的區別僅在與傳遞參數的不同,除綁定對象外,apply傳遞的是一個數組,而call傳遞的是參數列表。
例:
var name = "windowName";
var obj = {
name: 'innerName',
fn: function(a,b){
console.log(this.name);
console.log(a+b);
}
}
var a = obj.fn;
a.apply(obj,[1,2]) // apply寫法
a.call(obj,1,2) // call寫法
面試考點2: apply,call和bind有什麼區別?
調用bind時,會創建一個新的函數,必須要我們手動執行
三、函數的調用方式
- 作爲一個函數調用
- 函數作爲方法調用
- 使用構造函數調用函數
- 作爲函數方法調用函數
new 的過程: