學習javascript過程中遇到的that=this的調查解惑
最近寫微信小程式就在學js,關於關鍵字this的指向不是很理解,於是在收集了一系列資料以後做了以下整理,便於自己加深理解。
首先是這個講解的比較全:JavaScript中的物件查詢 他說這麼說的:
首先,this物件只會在一個函式中需要確定,如果是在全域性域下,this永遠為Global物件,在瀏覽器中通常就是window物件。而在javascript中,函式的呼叫一共有4種方式:
- Function Invocation Pattern
在這種模式下,foo函式體中的this永遠為Global物件,在瀏覽器中就是window物件。
- Method Invocation Pattern
在這種模式下,bar函式體中的this永遠為**“.”或“[”前的那個物件**,如上例中就一定是foo物件。
- Constructor Pattern
在這種模式下,foo函式內部的this永遠是new foo()返回的物件。
- Apply Pattern
在這種模式下,call和apply的第一個引數就是foo函式體內的this,如果thisObject是null或undefined,那麼會變成Global物件。
應用以上4種方式,確定一個函式是使用什麼樣的Pattern進行呼叫的,就能很容易確定this是什麼。
這裡說到了4種呼叫模式,能明確分類的情況下this的指向就一目瞭然,但是你可能會說光看文字還是有點不太理解,那好,下面又有個大神是這麼解釋的:
JS(ES5)裡面有三種函式呼叫形式:
func(p1, p2)
obj.child.method(p1, p2)
func.call(context, p1, p2) // 先不講 apply
一般,初學者都知道前兩種形式,而且認為前兩種形式「優於」第三種形式。
從看到這篇文章起,你一定要記住,第三種呼叫形式,才是正常呼叫形式:
func.call(context, p1, p2)
其他兩種都是語法糖,可以等價地變為 call 形式:
func(p1, p2) 等價於
func.call(undefined, p1, p2)
obj.child.method(p1, p2) 等價於
obj.child.method.call(obj.child, p1, p2)
請記下來。(我們稱此程式碼為「轉換程式碼」,方便下文引用)
至此我們的函式呼叫只有一種形式:
func.call(context, p1, p2)
這樣,this 就好解釋了
this,就是上面程式碼中的 context。就這麼簡單。
this 是你 call 一個函式時傳的 context,由於你從來不用 call 形式的函式呼叫,所以你一直不知道。
先看 func(p1, p2) 中的 this 如何確定:
當你寫下面程式碼時
function func(){
console.log(this)
}
func()
等價於
function func(){
console.log(this)
}
func.call(undefined) // 可以簡寫為 func.call()
按理說打印出來的 this 應該就是 undefined 了吧,但是瀏覽器裡有一條規則:
如果你傳的 context 就 null 或者 undefined,那麼 window 物件就是預設的 context(嚴格模式下預設 context 是 undefined)
因此上面的列印結果是 window。
如果你希望這裡的 this 不是 window,很簡單:
func.call(obj) // 那麼裡面的 this 就是 obj 物件了
再看 obj.child.method(p1, p2) 的 this 如何確定
var obj = {
foo: function(){
console.log(this)
}
}
obj.foo()
按照「轉換程式碼」,我們將 obj.foo() 轉換為
obj.foo.call(obj)
好了,this 就是 obj。搞定。
以上的文字裡面講到了語法糖和函式轉換,各種模式都可以轉換成Apply Pattern,然後就很簡單的取出第一個引數即可。但是他沒有說到Constructor Pattern的這個模式,但這個好理解:“foo函式內部的this永遠是new foo()返回的物件”,而且有new這個關鍵字在幫你區分。
更多的擴充套件那個大神也說到了:你怎麼還沒搞懂 this?
說了那麼多,還沒說為什麼要用that=this。這麼搞的目的就是在操作this的內容之前先把當前狀態備份下來。