JavaScript 中的箭頭函數與 `this` 函數:完整指南

  • 的價值 this 在 JavaScript 中,這取決於函數的呼叫方式,包括全域上下文、物件方法和嚴格模式的使用。
  • 箭頭函數不會創建自己的 this相反,它們從詞法上繼承了定義它們的領域的術語,這避免了回調函數中的許多問題。
  • 建議在回調函數和陣列方法中使用箭頭函數,但應避免將其用作物件方法、建構函數或需要 DOM 事件處理程序的函數。 this 動態的。

JavaScript 中箭頭和 this 函數的圖示

如果你使用 JavaScript 程式設計已經有一段時間了,你一定會認出這個關鍵字。 這讓你頭痛不只一次。自從 ES6 中出現了箭頭函數之後,事情變得更加複雜……或者更簡單,這取決於你如何看待它。

本文將深入探討其運作方式。 這適用於傳統函數和箭頭函數。為什麼有時它似乎指向特定對象,有時又指向全域對象?在什麼情況下使用箭頭函數是合理的,在什麼情況下最好避免使用箭頭函數?

到底是什麼 this 在 JavaScript 中

保留字 this 它指的是執行上下文 目前正在執行的函數。與其他語言不同,在 JavaScript 中,判斷是否執行函數並非基於函數的定義位置,而是基於函數的執行情況。 如何調用.

這意味著同一個函數可以用不同的方式調用,並且在每一種調用方式中, this 可以指向不同的對象這不是你能透過直接指派來改變的事情(你不能這樣做)。 this = algo但你可以透過一些特定機制來影響它,例如: call, apply y bind.

此外,它們的行為也各不相同。 嚴格模式和非嚴格模式在非嚴格模式下,如果您呼叫一個「裸」函數(前面沒有物件), this 它通常是全域物件(在瀏覽器中, window),而在嚴格模式下,它可以是 undefined在比較來自不同來源的程式碼範例時,這種差異非常重要。

這在全球背景下以及正常功能中都是如此。

在瀏覽器中,當您不在任何模組或函數內部時,全域上下文就是物件。 window在那裡 this 指向該對象也就是說,如果您在控制台中輸入以下內容:

console.log(this === window); // true en un entorno de navegador no estricto

在以“經典”方式(普通函數)聲明的函數中,其值為 this 這取決於該函數叫什麼。如果在非嚴格模式下呼叫它而沒有預先傳入對象,則不會出現此問題。 this 它通常是全球性的,嚴格來說,它將是 undefined這就是為什麼有時在將程式碼從一個站點遷移到另一個站點時, 這已經不是你所預期的了。.

這是用普通函數定義的物件方法。

當您使用傳統語法在物件上定義方法時, this 在方法內部,引用物件本身 該方法正是從這裡呼叫的。

例如,如果您有類似這樣的內容:

const obj = {
  speak() {
    console.log(this);
  }
};
obj.speak();

電話 obj.speak() 使 thisspeak 成為那個人 obj這是人們通常憑直覺所期望的行為:該方法「代表」對象說話。

如果使用經典函數而不是簡寫語法,效果是一樣的,因為 關鍵在於如何呼叫該方法。無論你使用方法縮寫還是關鍵字,都無關緊要。 function 物體內部。

這是用箭頭函數定義的方法。

當你使用箭頭函數定義方法時,情況就有所不同了。例如:

const obj2 = {
  speak: () => {
    console.log(this);
  }
};
obj2.speak();

在這種情況下,執行 obj2.speak() 你會看到 this 它不再是 obj2但外在詞彙語境 對於該對象,在經典的瀏覽器腳本中通常是全域對象。 window.

第一次看到這種情況會感到困惑,因為你通常會認為物件的方法應該指向物件本身。然而, 箭頭函數不會創建自己的 this它們繼承了以下價值 this 它們的作用域取決於其定義所在的範圍。如果定義範圍是全域的,則它們繼承全域作用域;如果定義範圍是其他的,則它們繼承該其他作用域。

因此,在現代文件中經常出現的一條建議是: 不要將箭頭函數用作物件方法 當你需要的時候 this 瞄準那個物體。

詞彙範圍 this 箭頭函數

普通函數和箭頭函數的主要區別在於後者 具有詞彙聯繫 this簡而言之:他們不決定他們的 this 不是在他們互相打電話的時候,而是在他們 創建.

想像一下這個例子:

const obj3 = {
  speak() {
    (() => {
      console.log(this);
    })();
  }
};
obj3.speak();

在這裡,似乎正如在…之內 speak 我們執行一個箭頭函數, 這應該會“重置”到全域狀態。但結果恰恰相反:箭頭函數捕獲了 this 它周圍的函數在這種情況下,該方法 speak 被調用為 obj3.speak()因此,該值 this 控制台上顯示的是來自 obj3.

我的意思是, 箭頭函數沒有自己的 this而是利用它們周圍環境的資源。這在嵌套回調、定時器、Promise 以及其他任何使用傳統函數時需要費力處理的地方都非常有用。 .bind 或是用一些技巧,例如 const that = this;.

遺失和保存的實際例子 this

JavaScript 中的一個經典問題是,當在方法內部定義函數時, 你失去了對…的引用 this 指向該物體 最終你會得到全球統一的版本,或者… undefined.

讓我們以使用情況的典型例子為例。 setTimeout 在具有傳統函數的物件的方法中:

const persona = {
  nombre: 'Agustin',
  decirNombre: function() {
    setTimeout(function() {
      console.log(this.nombre);
    }, 3000);
  }
};
persona.decirNombre(); // Muestra undefined

這裡 這是傳遞給函數的一部分 setTimeout 它不再是對象了 persona此回調函數在全域上下文中執行(在瀏覽器中, window), 所以 this.nombre 它試圖讀取全域變數中不存在的屬性,結果失敗了。 undefined.

在箭頭函數出現之前,常見的解決方案是儲存值 this 在輔助變數中 將其“拖”入函數中:

const persona = {
  nombre: 'Agustin',
  decirNombre: function() {
    let that = this; // aquí this es persona
    setTimeout(function() {
      console.log(that.nombre);
    }, 3000);
  }
};

多虧了這個變量,才能保持對物件的正確引用。但這是一種略顯笨拙且重複的技巧。使用箭頭函數,這個問題就大大簡化了:

const persona = {
  nombre: 'Agustin',
  decirNombre: function() {
    setTimeout(() => {
      console.log(this.nombre);
    }, 3000);
  }
};

這裡箭頭函數不會創建自己的元素。 this,所以 繼承了 this 該方法 decirNombre即對象 persona結果:「Agustin」無需中間變數即可正確顯示。 .bind.

呼叫、應用和綁定:控制值 this

除了使用方法呼叫這種「自然」的方式來設定上下文之外,JavaScript 還為我們提供了其他工具來… 強制設定該值 this 在正常功能中: call, apply y bind.

方法 call() y apply() 它們會立即呼叫該函數,讓您可以傳遞要使用的物件。 this。 區別在於 call 逐一接收參數,同時 apply 它們以數組形式接收。 bind()相反,它返回一個新函數,該函數具有 this “附加”到您指定的值所以你可以稍後在方便的時候打電話給她。

然而,對於箭頭函數,這些方法對於更改參數並不適用。 this 因為它的價值與詞彙有關。 您可以使用 call, apply o bind 可以傳遞參數,但不能修改箭頭函數的上下文,這與常規函數有一個非常重要的區別。

箭頭函數的基本語法

超越行為 this箭頭函數提供了一種 更簡潔、更具表現力的文法 在許多情況下,通用形式為:

(arg1, arg2, ..., argN) => expresion

此表單會自動傳回箭頭右側表達式的結果,因此 無需寫出這個詞 return 當你只有一個簡單的表達式。

一些常見的語法要點:

  • 無參數:
    () => 42 甚至 _ => 42 如果你不在乎參數名稱的話。
  • 僅需一個參數:
    括號是可選的;你可以這樣寫: x => x * 2 o (x) => x * 2.
  • 具有多個參數:
    括號是必需的: (x, y) => x + y.

當您需要多個語句時,可以使用 區塊體 帶鑰匙:

const sumar = (x, y) => {
  const resultado = x + y;
  return resultado;
};

在這種情況下,由於存在密鑰, 不再存在任何隱式回報如果你不放 return該函數將返回 undefined這適用於箭頭函數和傳統函數。

使用箭頭函數傳回字面對象

有一個雖小但非常常見的語法細節:當箭頭函數傳回一個 字面對象直接必須將其用括號括起來,以免解釋器將其誤認為程式碼區塊。

例如:

x => ({ y: x })

如果沒有這些圓括號,JavaScript 會將花括號解釋為函數體的開頭,而不是物件。這是一個簡單的技巧,但如果你忘記了它,就會導致很多愚蠢的錯誤。

箭頭函數:匿名且無需原型

箭頭函數是 語法匿名它們沒有正式名稱,這可能會使事情變得有些複雜。 偵錯和錯誤訊息因為在追蹤資訊中,除非你將其指派給一個具有可識別名稱的常數,否則你不會直接看到函數標識符。

此外,箭頭函數 他們不擁有房產 prototype 它們不能用作建築公司如果你試圖用以下方式召喚它們 new你會收到錯誤提示。要使用建構子或類別建立對象,仍然需要使用常規函數或語法。 class.

另一個後果是 它們不適用於需要內部自引用的模式。例如某些形式的遞歸或事件處理程序需要使用以下方式取消訂閱: this 或函數本身的名稱。

箭頭函數大放異彩

箭頭函數的最大優勢恰恰在於它們的 詞彙連結 this當您希望傳遞給另一個函數的回呼函數保持其原始特性時,它們是理想的選擇。 this 週邊地區。

例如,在一個包含啟動計時器的方法的物件中,需要不斷訪問 使用物件本身的屬性 this:

const contador = {
  id: 42,
  iniciar() {
    setTimeout(() => {
      console.log(this.id); // this es contador
    }, 1000);
  }
};

在 ES5 中,通常需要這樣做。 .bind(this) 回調或儲存 this 在另一個變數中。使用箭頭函數時, 程式碼變得更簡潔,更接近實際意圖。.

它們在數組方法方面也非常實用,例如 map, filter, reduce 以及公司,因為 減少句法噪聲 當函數邏輯簡短時:

const numeros = [1, 2, 3];
const dobles = numeros.map(n => n * 2);

巧妙運用這些緊湊的形狀,可以讓資料流一目了然,更容易理解。

何時避免使用箭頭函數

雖然箭頭函數非常有用,但它們並不能取代常規函數。以下幾個例子可以清楚說明這一點: 最好不要使用它們。:

  • 依賴物件方法的 this:
    如果你將方法定義為 saltos: () => { this.vidas--; } 物體內部 gato, this 它不會指向貓本身,而是指向外部環境,房產也不會像你預期的那樣改善。
  • 需要 DOM 事件回調的回調 this 動態的:
    在類似這樣的處理程序中 boton.addEventListener('click', () => { this.classList.toggle('on'); });, this 導致類型錯誤的可能不是按下的按鈕,而是更高層級的上下文。
  • 需要建構器或函數 prototype:
    由於它不能與…一起使用 new箭頭函數不適用於建立實例或基於原型的模式。

在所有這些情況下, 正常功能仍然是合適的工具 因為它允許這樣做 this 它與你呼叫函數的方式動態關聯。

如果你習慣根據自身需求有意識地在普通功能和箭頭功能之間進行選擇,那麼 this 根據上下文來看, 你的程式碼將更具可預測性和可讀性。 之後保存它的人使用。

最終,理解價值如何 this 在 JavaScript 中,箭頭函數如何繼承該值 要不再為意外結果而苦惱,充分利用 ES6 的語法糖,並編寫出完全符合你心意的方法、回調和事件處理程序,關鍵在於理解它們被創建的詞法作用域。