2024-11-22 vue 之 this 的上下文問題

2024-11-22 vue 之 this 的上下文問題

在 JavaScript 中,this 是一個特殊的關鍵字,它的值取決於函式的執行上下文(Context)。上下文可以理解為函式執行時的「當下環境」,this 在不同上下文中可能指向不同的物件。這就是所謂的 上下文問題


上下文問題簡單舉例

例子 1:普通函式的上下文

1
2
3
4
5
function sayHello() {
console.log(this);
}

sayHello(); // 在瀏覽器中,this 指向全局物件 window
  • 在這個例子中,函式 sayHello 是以普通方式調用。
  • 在瀏覽器中,普通函式內的 this 默認指向 window(Node.js 中是 global)。

例子 2:方法的上下文

1
2
3
4
5
6
7
8
const person = {
name: 'Alice',
sayHello: function () {
console.log(this.name);
}
};

person.sayHello(); // 輸出: Alice
  • 這裡的 sayHelloperson 物件的方法
  • 調用 person.sayHello() 時,this 指向 person

例子 3:函式內部嵌套的上下文

當嵌套函式使用普通函式語法時,內層函式的 this 不再指向外層函式的上下文,而是回到全局上下文。

1
2
3
4
5
6
7
8
9
10
11
const person = {
name: 'Alice',
sayHello: function () {
function nestedFunction() {
console.log(this); // 這裡的 this 指向全局物件
}
nestedFunction();
}
};

person.sayHello();

輸出:

1
window (在瀏覽器中)

在 Vue 中的上下文問題

在 Vue 中,我們通常希望 this 始終指向 Vue 實例,但如果你用傳統函式語法(function)而不是箭頭函式,可能會引發上下文問題。

例子:在 Vue 中失去上下文

1
2
3
4
5
6
7
methods: {
startTimer: function () {
setTimeout(function () {
console.log(this); // 這裡的 this 指向 window
}, 1000);
}
}

在這裡:

  • setTimeout 的回調函式是以普通方式執行的。
  • 回調函式內的 this 指向全局物件(window),而不是 Vue 實例。

解決方法:使用箭頭函式

箭頭函式不會創建自己的上下文,會繼承定義它的外層上下文的 this

1
2
3
4
5
6
7
methods: {
startTimer: function () {
setTimeout(() => {
console.log(this); // 這裡的 this 指向 Vue 實例
}, 1000);
}
}

為什麼上下文問題重要?

上下文問題直接影響到你的代碼能否正確運行,特別是當你需要訪問 Vue 實例的屬性或方法時。如果上下文丟失,this 會指向錯誤的物件,導致代碼報錯。

例子:上下文問題導致代碼報錯

1
2
3
4
5
6
7
methods: {
startTimer: function () {
setTimeout(function () {
this.time += 10; // 嘗試訪問 Vue 實例的 `time`,但報錯
}, 1000);
}
}

這裡會報錯:

1
TypeError: Cannot read properties of undefined (reading 'time')

原因是:

  • setTimeout 回調函式中的 this 不再指向 Vue 實例,而是 window
  • 所以 this.timeundefined

上下文問題的解決方法

  1. 使用箭頭函式:

    1
    2
    3
    4
    5
    6
    7
    methods: {
    startTimer: function () {
    setTimeout(() => {
    this.time += 10; // this 正確指向 Vue 實例
    }, 1000);
    }
    }
  2. 保存外層上下文:
    如果使用傳統函式,可以用變量保存 Vue 實例的 this

    1
    2
    3
    4
    5
    6
    7
    8
    methods: {
    startTimer: function () {
    const that = this; // 保存 Vue 實例的上下文
    setTimeout(function () {
    that.time += 10; // 使用保存的上下文
    }, 1000);
    }
    }

結論

上下文問題源於 JavaScript 的靈活性,this 的值根據函式執行方式而變化。在 Vue 中:

  1. 用箭頭函式是最簡單的解決方法。
  2. 確保 this 始終指向 Vue 實例是正確操作代碼的關鍵。
  3. 如果不了解上下文,代碼容易出現難以排查的錯誤。v