首先須要從函數的調用開始講起。瀏覽器
JS(ES5)裏面有三種函數調用形式:app
func(p1, p2)
obj.child.method(p1, p2)
func.call(context, p1, p2) // 先不講 apply
通常,初學者都知道前兩種形式,並且認爲前兩種形式「優於」第三種形式。函數
從看到這篇文章起,你必定要記住,第三種調用形式,纔是正常調用形式:this
func.call(context, p1, p2)
其餘兩種都是語法糖,能夠等價地變爲 call 形式:spa
func(p1, p2) 等價於
func.call(undefined, p1, p2)
obj.child.method(p1, p2) 等價於
obj.child.method.call(obj.child, p1, p2)
請記下來。(咱們稱此代碼爲「轉換代碼」,方便下文引用)code
至此咱們的函數調用只有一種形式:對象
func.call(context, p1, p2)
this,就是上面代碼中的 context。就這麼簡單。io
this 是你 call 一個函數時傳的 context,因爲你歷來不用 call 形式的函數調用,因此你一直不知道。console
先看 func(p1, p2) 中的 this 如何肯定:function
當你寫下面代碼時
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。搞定。
回到題目:
var obj = {
foo: function(){
console.log(this)
}
}
var bar = obj.foo
obj.foo() // 轉換爲 obj.foo.call(obj),this 就是 obj
bar()
// 轉換爲 bar.call()
// 因爲沒有傳 context
// 因此 this 就是 undefined
// 最後瀏覽器給你一個默認的 this —— window 對象
function fn (){ console.log(this) } var arr = [fn, fn2] arr[0]() // 這裏面的 this 又是什麼呢?
咱們能夠把 arr[0]( ) 想象爲arr.0( ),雖而後者的語法錯了,可是形式與轉換代碼裏的 obj.child.method(p1, p2) 對應上了,因而就能夠愉快的轉換了:
arr[0]() 假想爲 arr.0() 而後轉換爲 arr.0.call(arr) 那麼裏面的 this 就是 arr 了 :)
之後你遇到全部跟 this 有關的筆試題,都不會有疑問了。
完。