this 去哪? 本文寫於 2020 年 4 月 26 日 最後兩行函數的值為什麼不一樣??? 之前關於函數的文章里寫過了, 可以讓 和`obj.foo()`等價,那為什麼 this 指向不一樣? 在學 React 的時候,很多人會發現有個很煩人的操作,就是在 裡面,需要 。 首先需要從函數的調用開 ...
this 去哪?
本文寫於 2020 年 4 月 26 日
let obj = {
foo() {
console.log(this)
},
}
let bar = obj.foo
obj.foo() // 列印出的 this 是 obj
bar() // 列印出的 this 是 window
最後兩行函數的值為什麼不一樣???
之前關於函數的文章里寫過了,let bar = obj.foo
可以讓bar()
和obj.foo()
等價,那為什麼 this 指向不一樣?
在學 React 的時候,很多人會發現有個很煩人的操作,就是在constructor
裡面,需要bind(this)
。
首先需要從函數的調用開始講起。
ES5 裡面有三種函數調用形式:
foo(p1, p2)
console.log('================')
obj.foo(p1, p2)
console.log('================')
foo.call(obj, p1, p2)
/*
* 或者apply或者bind,這三個都是用來綁定this的
*/
乍看之下,前兩種簡單,後面一種複雜。很多人就選擇完全不使用後面的call()
。
但實際上,後面的才是我們真正的寫法,前面的都可以等價的變成該種寫法:
func.call(context, p1, p2)
foo(p1, p2)
等價於 foo.call(undefined, p1, p2)
obj.foo(p1, p2)
等價於 obj.foo.call(obj, p1, p2)
很多人特別喜歡死記硬背這個 this 的指向,“誰調用 this 就指向誰”blablabla……
但實際上,this 是要為我們所用的,我想讓你指向誰,你就要指向誰。我們不應該被它牽著走。
先看foo(p1, p2)
中的 this 如何確定。
當你寫下麵代碼時:
function foo() {
console.log(this)
}
foo()
等價於
function foo() {
console.log(this)
}
foo.call(undefined) // 可以簡寫為 foo.call()
按理說列印出來的 this 應該就是 undefined 了吧,但是瀏覽器里有一條規則:
如果你傳的第一個參數是null
或者undefined
, window 對象就是預設的。因此上面的列印結果是 window。
如果希望這裡的 this 不是 window 該怎麼做呢?
很簡單:
func.call(obj) // 那麼裡面的 this 就是 obj 對象了
再看obj.foo(p1, p2)
里的 this 如何確定:
let obj = {
foo() {
console.log(this)
},
}
obj.foo()
他本身的指向就是 obj 自己,所以寫成call()
也就是:obj.foo.call(obj)
回到最上面的代碼。
let obj = {
foo() {
console.log(this)
},
}
let bar = obj.foo
obj.foo() // 轉換為 obj.foo.call(obj),this 就是 obj
bar()
// 轉換為 bar.call()
// 由於沒有傳 context
// 所以 this 就是 undefined
// 最後瀏覽器給你一個預設的 this —— window 對象
提一個有趣的用法:如果把函數放到數組裡,然後數組[]調用函數,this 指向哪裡呢?
function fn() {
console.log(this)
}
let arr = [fn, fn2]
arr[0]()
這裡面的 this 是什麼呢?
我們可以把arr[0]()
理解為arr.0()
,雖然後者的語法錯了,但是形式與轉換代碼里的obj.foo(p1, p2)
對應上了!
arr[0]()
=> arr.0()
=> arr.0.call(arr)
那麼裡面的 this 就是 arr
了。
總之,this 就是你 call 一個函數時,傳入的第一個參數。
(完)