箭頭函數和常規函數之間的 5 個區別

2021-02-14 前端先鋒

在 JavaScript 中,你可以通過多種方式去定義函數。

第一種常用的方法是使用關鍵字 function:

// 函數聲明
function greet(who) {
  return `Hello, ${who}!`;
}
// 函數表達式
const greet = function(who) {
  return `Hello, ${who}`;
}

代碼中的函數聲明和函數表達式被稱為「常規函數」。

從 ES2015 開始,第二種可用的方法是 箭頭函數 語法:

const greet = (who) => {
  return `Hello, ${who}!`;
}

雖然兩者的語法都能夠定義函數,但是在開發時該怎麼選擇呢?這是個好問題。

在本文中,我將展示兩者之間的主要區別,以供你能夠根據需要選擇正確的語法。

1. this 值1.1常規函數

在常規 JavaScript 函數內部,this 值(即執行上下文)是動態的。

動態上下文意味著 this 的值取決於如何調用函數。在 JavaScript 中,有 4 種調用常規函數的方式。

簡單調用過程中,this 的值等於全局對象(如果函數在嚴格模式下運行,則為 undefined ):

function myFunction() {
  console.log(this);
}

// 簡單調用
myFunction(); // logs global object (window)

方法調用過程中,this 的值是擁有該方法的對象:

const myObject = {
  method() {
    console.log(this);
  }
};
// 方法調用
myObject.method(); // logs "myObject"

在使用 myFunc.call(context, arg1, ..., argN) 或 myFunc.apply(context, [arg1, ..., argN]) 的間接調用中,this 的值等於第一個參數:

function myFunction() {
  console.log(this);
}

const myContext = { value: 'A' };

myFunction.call(myContext);  // logs { value: 'A' }
myFunction.apply(myContext); // logs { value: 'A' }

在使用關鍵字 new 的構造函數調用期間,this 等於新創建的實例:

function MyFunction() {
  console.log(this);
}

new MyFunction(); // logs an instance of MyFunction

1.2箭頭函數

箭頭函數中 this 的行為與常規函數的 this 行為有很大不同。

無論如何執行或在何處執行,箭頭函數內部的 this 值始終等於外部函數的 this 值。換句話說,箭頭函數可按詞法解析 this,箭頭函數沒有定義自己的執行上下文。

在以下示例中,myMethod() 是箭頭函數 callback() 的外部函數:

const myObject = {
  myMethod(items) {
    console.log(this); // logs "myObject"    
    const callback = () => {
      console.log(this); // logs "myObject"    
    };
    items.forEach(callback);
  }
};

myObject.myMethod([1, 2, 3]); 

箭頭函數 callback()  中的 this 值等於外部函數  myMethod() 的 this。

this 詞法解析是箭頭函數的重要功能之一。在方法內部使用回調時,要確保箭頭函數沒有定義自己的 this:不再有 const self = this 或者 callback.bind(this) 這種解決方法。

2.構造函數2.1 常規函數

如上一節所述,常規函數可以輕鬆的構造對象。

例如用 Car() 函數創建汽車的實例:

function Car(color) {
  this.color = color;
}

const redCar = new Car('red');
redCar instanceof Car; // => true

Car 是常規函數,使用關鍵字 new 調用時會創建 Car 類型的新實例。

2.2 箭頭函數

this 詞法解決了箭頭函數不能用作構造函數。

如果你嘗試調用帶有 new 關鍵字前綴的箭頭函數,則 JavaScript 會引發錯誤:

const Car = (color) => {
  this.color = color;
};

const redCar = new Car('red'); // TypeError: Car is not a constructor 

調用 new Car('red')(其中 Car 是箭頭函數)會拋出 TypeError: Car is not a constructor。

3. arguments 對象3.1 常規函數

在常規函數的主體內部,arguments 是一個特殊的類似於數組的對象,其中包含被調用函數的參數列表。

讓我們用 3 個參數調用 myFunction 函數:

function myFunction() {
  console.log(arguments);
}

myFunction('a', 'b'); // logs { 0: 'a', 1: 'b'}

類似於數組對象的 arguments 中包含調用參數:'a' 和 'b'。

3.2箭頭函數

另一方面,箭頭函數內部未定義 arguments 特殊關鍵字。

用詞法解析 arguments 對象:箭頭函數從外部函數訪問 arguments。

讓我們試著在箭頭函數內部訪問 arguments:

function myRegularFunction() {
  const myArrowFunction = () => {    
      console.log(arguments);  
  }
  myArrowFunction('c', 'd');
}

myRegularFunction('a', 'b'); // logs { 0: 'a', 1: 'b' }

箭頭函數 myArrowFunction() 由參數  'c', 'd' 調用。在其主體內部,arguments 對象等於調用 myRegularFunction() 的參數:'a', 'b'。

如果你想訪問箭頭函數的直接參數,可以使用剩餘參數 ...args:

function myRegularFunction() {
  const myArrowFunction = (...args) => {    
      console.log(args);  
  }
  myArrowFunction('c', 'd');
}

myRegularFunction('a', 'b'); // logs { 0: 'c', 1: 'd' }

剩餘參數 ... args  接受箭頭函數的執行參數:{ 0: 'c', 1: 'd' }。

4.隱式返回4.1常規函數

使用 return expression 語句從函數返回結果:

function myFunction() {
  return 42;
}

myFunction(); // => 42

如果缺少 return 語句,或者 return 語句後面沒有表達式,則常規函數隱式返回 undefined:

function myEmptyFunction() {
  42;
}

function myEmptyFunction2() {
  42;
  return;
}

myEmptyFunction();  // => undefined
myEmptyFunction2(); // => undefined

4.2箭頭函數

可以用與常規函數相同的方式從箭頭函數返回值,但有一個有用的例外。

如果箭頭函數包含一個表達式,而你省略了該函數的花括號,則將顯式返回該表達式。這些是內聯箭頭函數

const increment = (num) => num + 1;

increment(41); // => 42

increment() 僅包含一個表達式:num + 1。該表達式由箭頭函數隱式返回,而無需使用 return 關鍵字。

5. 方法5.1 常規函數

常規函數是在類上定義方法的常用方式。

在下面 Hero 類中,用了常規函數定義方法 logName():

class Hero {
  constructor(heroName) {
    this.heroName = heroName;
  }

  logName() {    console.log(this.heroName);  }}

const batman = new Hero('Batman');

通常把常規函數用作方法。

有時你需要把該方法作為回調提供給 setTimeout() 或事件監聽器。在這種情況下,你可能會很難以訪問 this 的值。

例如用  logName()  方法作為 setTimeout() 的回調:

setTimeout(batman.logName, 1000);
// after 1 second logs "undefined"

1 秒鐘後,undefined 會輸出到控制臺。setTimeout()執行 logName 的簡單調用(其中 this 是全局對象)。這時方法會與對象分離。

讓我們手動把 this 值綁定到正確的上下文:

setTimeout(batman.logName.bind(batman), 1000);
// after 1 second logs "Batman"

batman.logName.bind(batman) 將 this 值綁定到 batman 實例。現在,你可以確定該方法不會丟失上下文。

手動綁定 this 需要樣板代碼,尤其是在你有很多方法的情況下。有一種更好的方法:把箭頭函數作為類欄位。

5.2 箭頭函數

感謝「類欄位提案」(https://github.com/tc39/proposal-class-fields)(目前在第3階段),你可以將箭頭函數用作類中的方法。

與常規函數相反,現在用箭頭定義的方法能夠把 this 詞法綁定到類實例。

讓我們把箭頭函數作為欄位:

class Hero {
  constructor(heroName) {
    this.heroName = heroName;
  }

  logName = () => {    
      console.log(this.heroName);  
  }
}

const batman = new Hero('Batman');

現在,你可以把 batman.logName  用於回調而無需手動綁定 this。 logName() 方法中 this 的值始終是類實例:

setTimeout(batman.logName, 1000);
// after 1 second logs "Batman"

6. 總結

了解常規函數和箭頭函數之間的差異有助於為特定需求選擇正確的語法。

常規函數中的 this 值是動態的,並取決於調用方式。是箭頭函數中的 this 在詞法上是綁定的,等於外部函數的 this。

常規函數中的 arguments 對象包含參數列表。相反,箭頭函數未定義 arguments(但是你可以用剩餘參數 ...args 輕鬆訪問箭頭函數參數)。

如果箭頭函數有一個表達式,則即使不用 return 關鍵字也將隱式返回該表達式。

最後一點,你可以在類內部使用箭頭函數語法定義去方法。粗箭頭方法將 this 值綁定到類實例。

不管怎樣調用胖箭頭方法,this 始終等於類實例,在回調這些方法用時非常有用。

相關焦點

  • ES6 箭頭函數大起底
    `;}const sayHi = function(someone) { return `Hello, ${someone}`;}上面的函數聲明和函數表達式,我們姑且稱之為常規函數。還有就是 ES6 新增的箭頭函數語法:const sayHi = (someone) => { return `Hello, ${someone}!`;}既然常規形式和箭頭語法都能定義函數,我們該如何選擇呢?
  • 你知道ES6箭頭函數的優缺點嗎?
    `;}// 函數表達式const sayHi = function(someone) {  return `Hello, ${someone}`;}上面的函數聲明和函數表達式,我們姑且稱之為常規函數。還有就是 ES6 新增的箭頭函數語法:const sayHi = (someone) => {  return `Hello, ${someone}!`;}既然常規形式和箭頭語法都能定義函數,我們該如何選擇呢?這篇文章總結了它們之間的關鍵區別,下次你選擇的時候心裡就有數了。
  • 什麼是JavaScript「箭頭函數」?
    ,聽起來好NB,但是如果你知道它是因為使用了=>這樣類似箭頭的符號 ,所以才叫箭頭函數。瞬間感覺:「呵,這名字起的...」。es6增加了箭頭函數的特性當然不是為了裝B,它可以看作是js中函數的一次進化。我們知道,在js中,函數有著不一樣的地位,除了作為普通函數外,它可以用來構造自定義類型,作為對象的方法,通過調用bind方法得到一個新函數。
  • 編寫高質量箭頭函數的5個最佳做法
    作者:Dmitri Pavlutin譯者:前端小智來源:dmitripavlutin.com箭頭功能值得流行。它的語法簡潔明了,使用詞法綁定綁定 this,它非常適合作為回調。在本文中,通過了解決學習5個最佳實踐,以便我們可以從中學習更多箭頭函數的知識,並從它身上獲得更多的好處。1.
  • 什麼是JavaScript「箭頭函數」?初學者必看
    ,聽起來好NB,但是如果你知道它是因為使用了=>這樣類似箭頭的符號 ,所以才叫箭頭函數。瞬間感覺:「呵,這名字起的...」。es6增加了箭頭函數的特性當然不是為了裝B,它可以看作是js中函數的一次進化。我們知道,在js中,函數有著不一樣的地位,除了作為普通函數外,它可以用來構造自定義類型,作為對象的方法,通過調用bind方法得到一個新函數。
  • JavaScript初學者必看「箭頭函數」
    譯者按:箭頭函數看上去只是語法的變動,其實也影響了this的作用域。本文我們介紹箭頭(arrow)函數的優點。更簡潔的語法我們先來按常規語法定義函數:function funcName(params) {   return params + 2; }funcName(2);// 4該函數使用箭頭函數可以使用僅僅一行代碼搞定!
  • 【JavaScript】箭頭函數
    箭頭函數箭頭函數是在es6中引入的,用法十分簡單,允許使用 => 定義函數// 箭頭函數const fn = (
  • 箭頭函數=> 的使用與局限 - ES6中JavaScript新特性之函數
    ,ES5 和 ES6 的`name`屬性返回的值不一樣。如果將一個具名函數賦值給一個變量,則 ES5 和 ES6 的`name`屬性都返回這個具名函數原本的名字。,分別使用了箭頭函數和普通函數。所以,箭頭函數轉成 ES5 的代碼如下。
  • JavaScript 箭頭函數的主要語法和用法
    作者簡要介紹了 JavaScript 箭頭函數的主要語法和用法,但是對於初學者,請一定要關注箭頭函數的 this問題,尤其是在寫 jQuery
  • 深入理解箭頭函數,學習其非常特殊且有用的特性
    深入理解箭頭函數讓我們深入研究一下箭頭函數。箭頭函數不僅僅是編寫簡潔代碼的「捷徑」。它還具有非常特殊且有用的特性。JavaScript 充滿了我們需要編寫在其他地方執行的小函數的情況。,所以其中的 this.title 其實和外部方法 showList 的完全一樣。
  • es6 箭頭函數後面的大括號
    reurn (r1 + r2)}let r1 = res(1,2);let r2 = res2(1,2);let r3 = res3(1,2);列印出來發現 r1,r3 正確的,r2為 undefined,箭頭函數後面不加大括號
  • Excel函數IF使用教程:利用if函數給相關數據添加箭頭標識?
    Excel函數IF使用教程:利用if函數給相關數據添加箭頭標識?19:41 來源:成都路凡教育 原標題: Excel函數
  • Destroy()函數 和 DestroyImmediate()函數的區別
    Destroy()函數 和 DestroyImmediate()函數的區別這篇文章的目標是了解Destroy()函數和DestroyImmediate()函數之間的區別。這兩個函數的最終作用是相同的,都是去銷毀遊戲物體,但唯有一點點小區別,我們去理解這個小區別是非常重要的,錯誤的使用這些函數可能會導致你的應用層序崩潰。下面,用一點理論,和測試代碼去理解它。讓我們看一下測試代碼:
  • 你知道R中的賦值符號箭頭(<-)和等號(=)的區別嗎?
    出於對某種賦值方式的偏好,甚至出現了等號黨和箭頭黨,但是到底孰好孰壞,顯然爭不出任何結果,相對來說更重要的是了解這兩者的區別。只有我們深刻理解了其相同與不同之後,才能更好的運用他們。R語言最開始設計的時候,是採用箭頭(<-)作為賦值符號的,這是從APL語言繼承而來的(箭頭表示賦值,等號表示判斷)。之後的S語言也沿用了這個用法,再之後R語言為了保持和S語言的兼容性保留了這個箭頭。
  • 內聯函數和外聯函數有什麼區別
    打開APP 內聯函數和外聯函數有什麼區別 發表於 2017-12-15 11:52:13   內聯函數與外聯函數的區別   在類聲明的內部聲明或定義的成員函數叫做內聯(INLINE)函數。
  • COUNT函數和COUNTA函數有什麼區別
    我們都知道count是計數,那count和COUNTA有什麼區別呢?今天小編就給大家介紹一下。;函數計算包含數字的單元格個數以及參數列表中數字的個數。value1 是必需參數。 要計算其中數字的個數的第一項、單元格引用或區域。value2, ... 為可選參數。 要計算其中數字的個數的其他項、單元格引用或區域,最多可包含 255 個。
  • [函數] strstr、strrchr、substr、stristr四個函數的區別
    最後感謝你的關注今天要分享的PHP函數: strstr、strrchr、substr、stristr 函數php中strstr strrchr substr stristr這四個字符串操作函數特別讓人容易混淆,常用的是substr,strstr,基本上可以滿足對字符串的操作。
  • excel函數COUNT的使用以及和函數COUNTA的區別
    excel中COUNT函數的功能就是統計出所選擇的區域範圍內單元格的值是數字的個數,但是對文字,錯誤值等其他類型的值的單元格不做統計。 COUNT函數中的參數所表示的含義
  • Excel函數中FIND和SEARCH兩者的區別?
    find和search兩個函數功能基本一致,不過兩者還是有區別的,它們之間的區別是什麼?下面來看看吧。1.首先我們打開excel表格,點擊公式,找到插入函數按鈕。2.然後輸入一個find函數,點擊轉到。
  • excel函數COUNT的使用以及和函數COUNTA的主要區別
    excel中COUNT函數的功能就是統計出所選擇的區域範圍內單元格的值是數字的個數,但是對文字,錯誤值等其他類型的值的單元格不做統計。COUNT函數中的參數所表示的含義COUNT函數的參數寫法不同表示的含義也不一樣例如=COUNT(C1:C10) 如果要是按這種寫法計算出來的值就是C1到C10中值是數字單元格的個數。=COUNT(C1,C10)如果是這種寫法計算出來的值就是C1和C10這兩個單元中值是數字的個數。