2026年5月4日 星期一

JavaScript 匿名函式作為物件方法回傳值時的 this 指向陷阱


在 JavaScript 中,匿名函式作為物件方法的回傳值時,其 this 的指向取決於「函式是如何被呼叫的」,而非定義位置。
  • 非嚴格模式(Non-strict mode):以普通函式(參考範例一的 t_run())呼叫時, this 指向全域物件(瀏覽器中為 window )。
  • 嚴格模式(Strict mode):this 會是 undefined。

## 範例一:觀察 this 的遺失

當我們將回傳的函式賦值給變數並執行時,它會變成一個「普通函式呼叫」。

<script type="text/javascript">
var name = "Tom"; // 全域變數

var objectA = {
  name: "John" ,
  test_1: function() {
   return function() {
    this.name = "Mary"; // 此時的 this 可能不如預期
    };
  }
};

console.log(window.name); // Tom
console.log(objectA.name); // John

var t_run = objectA.test_1();
t_run(); // 關鍵點:這是一個獨立函式呼叫,this 指向 window

console.log(window.name); // Mary (全域變數被修改了)
console.log(objectA.name); // John(物件屬性未變)
</script>

## 解決方案

1. 使用 .bind(this) 鎖定指向
.bind(this) 會建立一個新的函式,並將其 this 永久綁定到指定物件。

<script type="text/javascript">
test_1: function() {
 // 此處的 this 是 objectA,我們將它綁定給回傳的匿名函式
 return function() {
   this.name = "Mary";
 }.bind(this);
}
</script>

2. 使用 ES6 箭頭函式(推薦做法)
箭頭函式沒有自己的 this,它會捕捉定義時所在環境的 this (稱為 lexical this)。

<script type="text/javascript">
test_1: function() {
 return () => {
   this.name = "Mary"; // 自動繼承 test_1 執行時的 this(即 objectA)
  };
}
</script>

3. 使用變數暫存
在 ES6 普及前,開發者常用變數(如 self 或 that)來捕捉當前的 this。

<script type="text/javascript">
test_1: function() {
 var self = this; // 鎖住當前的 objectA
 return function() {
   self.name = "Mary";
 };
}
</script>

## 關鍵觀念:為什麼會這樣?

請記住這句話:this 的指向取決於呼叫方式,而不是函式定義的位置。
  • 程式碼 A(物件呼叫 vs 賦值後呼叫)
<script type="text/javascript">
var obj = {
  name: "john",
  fn: function() {
   console.log(this);
  }
};

// 物件呼叫
obj.fn(); // this -> obj

// 賦值後呼叫
var f = obj.fn;
f(); // this -> window(或 'use strict' -> undefined)
</script>
  • 程式碼 B(連續呼叫)
<script type="text/javascript">
var objectA = {
  name: "John" ,
  test_1: function(){
   return function(){
    console.log(this);
  };
 }
};

objectA.test_1()();
// 第一個 () 回傳匿名函式
// 第二個 () 是在全域執行該回傳函式 -> this 指向 window
</script>

結論:嚴格模式的行為
若開啟 'use strict';,範例一執行 trun() 時,this 會是 undefined。此時嘗試執行 this.name = "Mary" 會噴出 TypeError。這雖然會讓程式中斷,但能強迫開發者修正錯誤的 this 引用,是更安全的做法。




沒有留言:

張貼留言

請注意 : 您的留言發佈成功 , 需經審核後 , 才能決定是否回覆 . 謝謝 !!