在 JavaScript 中,this
總是令人困惑,它是 JavaScript 中最常見的陷阱之一。在 JavaScript 中,這不是一個好的設計(您可以參考 JavaScript 的其他一些設計缺陷這裡),由於它的惰性綁定特性,它可以是一個全域物件、當前物件,或是...。有些人甚至避免在 JavaScript 中使用 this
。
實際上,如果您掌握了 this
的工作原理,那麼您就會知道如何避開這些陷阱。讓我們看看在以下情況下 this
指向什麼。
1. 在全域範圍程式碼中
alert(this)//window
在全域範圍程式碼中,this
將指向全域物件(通常在 Web 瀏覽器中是 window
)。
2. 在純函數呼叫中
function fooCoder(x) { this.x = x; } fooCoder(2); alert(x);// Global variable x's value is 2
這裡 this
也指向全域物件,因為在全域範圍中定義的函數實際上是全域物件的一個方法。所以 this
將是全域物件。在嚴格模式下,this
將是 undefined
。
3. 在物件的方法呼叫中
var name = "clever coder"; var person = { name : "foocoder", hello : function(sth){ console.log(this.name + " says " + sth); } } person.hello("hello world");
輸出將是 "foocoder says hello world"。this
將指向 person
物件,也就是呼叫該方法的當前物件。
4. 在建構子中
new FooCoder();
在建構子中,this
將指向使用 new
新建立的物件。
5. 在私有函數呼叫中
var name = "clever coder"; var person = { name : "foocoder", hello : function(sth){ var sayhello = function(sth) { console.log(this.name + " says " + sth); }; sayhello(sth); } } person.hello("hello world");//clever coder says hello world
在私有函數中,this
不會綁定到外部方法的物件,而是綁定到全域物件。這被認為是 JavaScript 的設計缺陷,因為沒有人希望私有函數中的 this
在這裡指向全域物件。一般的解決方案是將 this
賦值給另一個變數,並在私有函數中引用該變數。
var name = "clever coder"; var person = { name : "foocoder", hello : function(sth){ var that = this; var sayhello = function(sth) { console.log(that.name + " says " + sth); }; sayhello(sth); } } person.hello("hello world");//foocoder says hello world
6. 在 call() 或 apply() 中
person.hello.call(person, "world");
apply()
和 call()
類似,唯一的區別是第一個參數之後傳入的參數。在 apply()
中,其他參數將以陣列形式傳入,而在 call()
中,其他參數將分別傳入。
call( thisArg [,arg1,arg2,… ] ); // Parameter list,arg1,arg2,... apply(thisArg [,argArray] ); // Parameter list,argArray
傳入的第一個參數是 this
將指向的物件。我們可以指定任何要讓 this
指向的物件。
7. 其他
我們可能會經常看到以下程式碼:
$("#some-ele").click = obj.handler;
如果我們在 handler
中使用 this
,this
會綁定到 obj
嗎?顯然不會,在賦值之後,函數是在回呼函數中呼叫的,this
將綁定到 $("#some-div")
元素。這是我們需要理解的——執行上下文。
那麼我們如何在回呼函數中指定 this
物件為我們想要的物件呢?在 ECMAScript 5 中,有一個 bind()
方法:
fun.bind(thisArg[, arg1[, arg2[, ...]]])
thisArg
將是我們想要成為的 this
物件。
$("#some-ele").click(person.hello.bind(person));
現在 this
將是 person
物件
在 Prototype.js 中,我們可以找到 bind()
的實現:
Function.prototype.bind = function(){ var fn = this, args = Array.prototype.slice.call(arguments), object = args.shift(); return function(){ return fn.apply(object, args.concat(Array.prototype.slice.call(arguments))); }; };
結論
1. 當函數作為物件的方法呼叫時,this
將指向該物件
2. 當在純函數呼叫中時,this
將是全域物件(在嚴格模式下,this
將是 undefined
)
3. 在建構子中,this
將是新建立的物件
一句話概括,this
總是會指向呼叫該函數的物件。