參考書籍: Javascript設計模式與開發實踐(曾探)
JavaScript 的this
總是指向一個對象,而具體指向哪個對象是在運行時基於函數的執行環境動態綁定的,而非函數被聲明時的環境。
不同情況下this
的指向:
- 作爲對象的方法調用。
- 作爲普通函數調用。
- 構造器調用。
- Function.prototype.call 或Function.prototype.apply 調用。
1、 作爲對象的方法調用
當函數作爲對象的方法被調用時,this 指向該對象:
var obj = {
a: 1,
getA: function(){
alert ( this === obj ); // 輸出:true
alert ( this.a ); // 輸出: 1
}
};
obj.getA();
2、作爲普通函數調用
當函數不作爲對象的屬性被調用時,也就是我們常說的普通函數方式,此時的this 總是指向全局對象。在瀏覽器的JavaScript 裏,這個全局對象是window 對象。
window.name = 'globalName';
var getName = function(){
return this.name;
};
console.log( getName() ); // 輸出:globalName
或者:
window.name = 'globalName';
var myObject = {
name: 'sven',
getName: function(){
return this.name;
}
};
var getName = myObject.getName;
console.log( getName() ); // globalName
e.g.
//錯誤方法
window.id = 'window';
document.getElementById( 'div1' ).onclick = function(){
alert ( this.id ); // 輸出:'div1'
var callback = function(){
alert ( this.id ); // 輸出:'window'
}
callback();
};
//正確方法:作爲變量的值保存
document.getElementById( 'div1' ).onclick = function(){
var that = this; // 保存div 的引用
var callback = function(){
alert ( that.id ); // 輸出:'div1'
}
callback();
};
//在ECMAScript 5 的strict 模式下,這種情況下的this 已經被規定爲不會指向全局對象,而是undefined:
function func(){
"use strict"
alert ( this ); // 輸出:undefined
}
func();
3、構造器調用
除了宿主提供的一些內置函數,大部分JavaScript 函數都可以當作構造器使用。構造器的外表跟普通函數一模一樣,它們的區別在於 被調用的方式。當用new 運算符調用函數時,該函數總會返回一個對象,通常情況下,構造器裏的this 就指向返回的這個對象.
var MyClass = function(){
this.name = 'sven';
};
var obj = new MyClass();
alert ( obj.name ); // 輸出:sven
但用new 調用構造器時,還要注意一個問題,如果構造器顯式地返回了一個object 類型的對象,那麼此次運算結果最終會返回這個對象,而不是我們之前期待的this:
var MyClass = function(){
this.name = 'sven';
return { // 顯式地返回一個對象
name: 'anne'
}
};
var obj = new MyClass();
alert ( obj.name ); // 輸出:anne
如果 構造器不顯式地返回任何數據,或者是返回一個非對象類型的數據,就不會造成上述問題:
var MyClass = function(){
this.name = 'sven'
return 'anne'; // 返回string 類型
};
var obj = new MyClass();
alert ( obj.name ); // 輸出:sven
4、Function.prototype.call 或Function.prototype.apply 調用
跟普通的函數調用相比,用Function.prototype.call 或Function.prototype.apply 可以動態地改變傳入函數的this:
var obj1 = {
name: 'sven',
getName: function() {
return this.name;
}
};
var obj2 = {
name: 'anne'
};
console.log( obj1.getName() ); // 輸出: sven
console.log( obj1.getName.call( obj2 ) ); // 輸出:anne