var obj = {
foo: function(){
console.log(this)
}
}
var bar = obj.foo
obj.foo() // 打印出的 this 是 obj
bar() // 打印出的 this 是 window
請解釋最後兩行函數的值爲什麼不一樣。
函數調用
首先需要從函數的調用開始講起。
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。搞定。