精簡討論Javascript 中的 this 問題
最近看了一些文章都有談論 this 的問題。其實,總覺得並沒想象那麼難(傲嬌臉...)。所以,我在此就想分享一下我的極簡理解,也希望能幫助到大家~~~~
情況一:誰呼叫,this 就指向那個物件。
話不多說,直接用例開幹。 例一:
// 1. 直接呼叫 function getAge() { var age = 18 console.log(this.age)// undefined console.log(this)// window } getAge() 複製程式碼
分析:getAge 是誰呼叫的?
- 寫完整的話應該是 window.getAge().
- 呼叫物件就是 window,所以函式內部上下文環境就是 window.
- 當前環境下沒有 window.age ,所以就是 undefined 啦.
注:嚴格模式下, window 環境下呼叫的函式的this 是 undefined,不指向 window。
'use strict' function getAge() { var age = 18 // console.log(this.age)// 直接語法報錯,undefined 無法再找屬性 console.log(this)// undefined } getAge() 複製程式碼
例二:
// 2. 物件呼叫 var obj = { age: 19, getAge() { console.log(this.age) console.log(this) }, returnGetAgeFunc() { return this.getAge } } obj.getAge() obj.returnGetAgeFunc()() 複製程式碼
首先是 obj.getAge(),答案是19
和obj
這個物件
- 這個 getAge 就是 obj,所以上下文環境是 obj 無可厚非。 所以第一個答案是 小明2,第二個答案是 obj 這個物件。
然後是 obj.returnGetAgeFunc() (), 答案是undefined
和window
obj.returnGetAgeFunc() === getAge
例三:
// 3. 巢狀深一層 var obj = { age: 19, innerObj: { getAge() { console.log(this.age) console.log(this) }, returnGetAgeFunc() { return this.getAge } } } obj.innerObj.getAge()// undefinedinnerObj obj.innerObj.returnGetAgeFunc()() 複製程式碼
分析:
- obj.innerObj 簡單來說就是 innerObj 物件, 然後用 innerObj呼叫函式,當前 this 不就是 innerObj嘛。所以,age 是 obj 的屬性,因此, this.age === undefined.this 指向就是 innerObj
- 第二個同理,obj.innerObj.returnGetAgeFunc() 這麼長一截就是為了取出 obj.innerObj 中的 getName 函式。然後在 Window 環境下執行。this.age === undefined, this 指向就是 Window。
再用程式碼等量代換一次...別嫌我囉嗦,因為,和一些人講過,但是他們沒懂..~。~
var obj = { age: 19, innerObj: { getAge() { console.log(this.age) console.log(this) }, returnGetAgeFunc() { return this.getAge } } } obj.innerObj.getAge() obj.innerObj.returnGetAgeFunc()() // 使用上完全等價下面 var innerObj: { getAge() { console.log(this.age) console.log(this) }, returnGetAgeFunc() { return this.getAge } } innerObj.getAge() function getAge() { console.log(this.age) console.log(this) } // obj.innerObj.returnGetAgeFunc() == getName getName()// obj.innerObj.returnGetAgeFunc()() 複製程式碼
情況二:自行改變當前環境下的this指向。
完全複製之前的事例,加一點東西來解釋,儘可能的減少過多冗餘資訊接收。
改變執行函式內部 this 指向的有三個函式, call, apply, bind. 這裡就不解釋這三個函式的差異了。預設大家都知道~
// 1. 直接呼叫 var obj = {} obj.age = 20 function getAge() { var age = 18 console.log(this.age)// 20 console.log(this)// obj } getAge.call(obj) 複製程式碼
其實挺容易理解的,就是把函式的執行上下文環境的 this 換成了 obj。
同理:
// 2. 物件呼叫 var tempObj = { age: 20 } var obj = { age: 19, getAge() { console.log(this.age) console.log(this) }, returnGetAgeFunc() { return this.getAge } } obj.getAge.call(tempObj)// 20tempObj obj.returnGetAgeFunc().call(tempObj)//20tempObj 複製程式碼
最後一個就不用多說了。這裡再補充一些。
比如通過 new 關鍵字得到的物件,呼叫內部函式時,this 指向的就是這個物件,其實就是使用了上述 call,apply,bind 函式來改變的。
情況三: ES6 箭頭函式
使用箭頭函式程式設計往往也被稱為函數語言程式設計,也是面向過程程式設計。 當我們從上往下的編寫業務的時候,很多情況下,我們希望this 的指向是一致的,特別是回撥函式中的 this。
因此,簡單來說,this 指向
// 2. 物件呼叫 var tempObj = { age: 20 } var obj = { nonArrowFunc() { // this === obj return function() { console.log(this)// tempObj console.log(this.age)// 20 } }, arrowFunc() { // this === obj return () => { console.log(this)// obj console.log(this.age)// undefined } } } obj.nonArrowFunc().call(tempObj) obj.arrowFunc().call(tempObj)//20tempObj 複製程式碼
沒有箭頭函式時,上下文環境被更換成了 tempObj 使用箭頭函式後,上下文環境使用執行環境的this.
再簡單的說就是,箭頭函式裡面的this 不用自己函式上下文環境中的,用上一級的。
總結
因此,我歸納的 this 就是這三種,一般業務情況下,我們都不會搞得很複雜,this指向有問題,我們也會用一個變數來承接解決。 但是,面試的時候可能會比較多層,比較繞。如果不熟練的話,只需要按照我的第二種情況去等量替換,你就容易得到結果啦~