初學JS的朋友可能對this指向問題有些困惑。最近在看《Javascript設計模式與開發實踐》一書,裡面總結得很到位。梳理下大致可以分為4情況,有興趣的可以自行去翻書閱讀。
作為物件的方法呼叫
作為普通函式呼叫
構造器呼叫
Function.prototype.call和Function.prototype.apply呼叫
1、作為物件的方法呼叫
當函式作為物件的方法被呼叫時,此時this指向該物件。
var obj = {
name: lq,
getName: function () {
console.log (this === obj); // true
console.log (this.name); // lq
}
}
複製程式碼
2、作為普通函式呼叫
當函式不作為物件屬性被呼叫,而是通過普通的函式呼叫,此時的this總是指向全域性物件。在瀏覽器環境的JavaScript裡,就是window物件。
window.name = 'lq';
let getName = function () {
return this.name;
}
console.log ( getName() ); // 輸出:lq 此時this指向window物件
複製程式碼
又比如:
window.name = 'lq';
let myObj = {
name: 'xiaoming',
getName: function () {
return this.name;
}
};
let getName = myObj.getName;
console.log ( getName() ); // 輸出:lq
複製程式碼
有時候在我們點選div節點的事件函式內部,如果有一個callback回撥函式,當callback被當作普通函式呼叫時,callback內部的this指向window,但實際上我們想讓它指向被點選的div節點本身。程式碼如下:
<html>
<body>
<div id='div1'>my is div node</div>
</body>
<script>
window.id = 'window';
document.querySelector('#div1').onclick = function () {
console.log (this.id); // 輸出:div1
let callback = function () {
console.log (this.id); // 輸出:window
}
callback();
}
</script>
</html>
複製程式碼
通常的解決方法是用一個變數來儲存div節點的引用:
document.querySelector('#div1').onclick = function () {
let _this = this;
let callback = function () {
console.log (_this.id); //輸出:div1
}
callback();
}
複製程式碼
另外,在ESMAScript5下的strict嚴格模式下,這種普通函式呼叫已經被規定為不會指向全域性物件,而是undefined。
function fun () {
'use strict'
console.log (this); //輸出了:undefined
}
fun();
複製程式碼
3、構造器呼叫
Javascript種沒有類的概念,但提供了new運算子,這樣我們可以從構造器中建立物件。除了宿主提供的一些內建函式,大部分的Javascript函式可以當作構造器使用。構造器跟普通函式沒什麼太大區別,唯一的區別是被呼叫的方式。
當用new運算子呼叫函式時,該函式會返回一個物件,一般情況下,構造器裡的this都指向返回的這個物件。如下:
let myObj = function () {
this.name = 'lq';
};
let obj = new myObj();
console.log (obj.name); //輸出:lq
複製程式碼
如果new構造器顯式地返回一個object型別的物件,那麼運算的結果就會最終返回這個物件,this就不是我們之前所期望的this了。如下:
let myObj = function () {
this.name = 'lq';
return {
name: 'xiaoming';
}
}
let obj = new myObj();
console.log(obj.name); // 輸出:xiaoming
複製程式碼
如果構造器返回的不是object型別的物件,而是一個非物件型別的資料,就沒有上面的問題。
let myObj = function () {
this.name = 'lq';
return 'xiaoming';
};
let obj = new myObj();
console.log (obj.name); //輸出:lq
複製程式碼
4、Function.prototype.call和Function.prototype.apply呼叫
這兩種方式呼叫可以動態地改變傳入函式的this:
let obj1 = {
name: 'lq',
getName: function () {
return this.name;
}
};
let obj2 = {
name: 'xiaoming'
};
console.log (obj1.getName()); //輸出:lq
console.log (obj1.getName.call(obj2)); // 輸出:xiaoming
console.log (obj1.getName.apply(obj2)); //輸出:xiaoming
複製程式碼