1. 程式人生 > 實用技巧 >JavaScript 類方法中的 this 指向問題

JavaScript 類方法中的 this 指向問題

一個簡單的 JavaScript 類示例

class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }
  speak() {
    console.log(this)
  }
}

const p = new Person('Callback', 27)
p.speak();

執行上面的示例可以看到控制檯輸出如下圖

接下來做一個操作:

const f = p.speak
f() // 輸出 undefined

這裡將常量 f 指向 speak() 方法,注意這裡是「指向」,並不是「呼叫」。之後呼叫 f()

可以觀察到控制檯輸出為 undefined。那麼問題來了,為什麼這麼賦值之後,函式內部的 this 不再是 Person 的例項 p 而變成了 undefined

實際上,方法內部的 this 永遠指向的是呼叫這個方法的物件,通過 Person 的例項 p 呼叫 speak() 方法,那麼 this 就指向 p。後面將 speak 賦值給常量 f 後, f() 表示的就是通過全域性的 window 物件來呼叫 f() 方法。按理說,f() 方法被呼叫時應該輸出 window 物件的資訊,但這裡輸出的卻是 undefined。這是因為根據 JavaScript 的語法規則,所有在類中定義的方法都預設開啟區域性嚴格模式

。在嚴格模式下,所有指向 window 物件的 this,都全部變更為 undefined。看下面的示例:

function demo() {
  console.log(this)
}
function demo2() {
  'use strict'
  console.log(this)
}
demo() // 輸出 window 物件資訊
demo2() // 輸出 undefined

回到最初的問題,之所以呼叫 f() 方法後輸出 undefined,是因為 f 指向的方法 speak() 是在 Person 類中定義的,其預設開啟了局部嚴格模式。當 f() 被呼叫時,呼叫方為 window

物件,這樣 this 就變成了 undefined

其實這一點和 Java 中對方法的處理是類似的。每一個例項物件都持有對方法的引用而不是儲存一份方法,方法會自動在引數列表的最前面新增一個 this 引數,這就是我們之所以能夠在 Java 方法裡使用 this 關鍵字的原因,而這個 this 指向的就是當前在呼叫這個方法的物件。區別在於,Java 裡所有的方法都不能脫離定義它的類,不可以像 JavaScript 裡面把類的某個方法賦值給一個全域性變數或常量。