JS 中的函式表示式和函式宣告你混淆了嗎?

前端小智發表於2022-12-16
微信搜尋 【大遷世界】, 我會第一時間和你分享前端行業趨勢,學習途徑等等。
本文 GitHub https://github.com/qq449245884/xiaozhi 已收錄,有一線大廠面試完整考點、資料以及我的系列文章。

在 JavaScript 中,function關鍵字可以完成一個簡單的工作:建立一個函式。 但是,使用關鍵字定義函式的方式可以建立具有不同屬性的函式。

在本文中,我們來看一下,如何使用function關鍵字來定義函式宣告和函式表示式,以及這兩種函式之間的區別又是什麼。

1.函式表示式vs函式宣告

函式宣告和函式表示式是使用 function 關鍵字建立函式的2種方法。

舉個例子來說明差異,我們建立兩個版本的 sums 函式:

function sumA(a, b) {
  return a + b;
}

(function sumB(a, b) {
  return a + b;
});

sumA(1, 2); // ???
sumB(1, 2); // ???

動手試試:https://jsfiddle.net/dmitri_p...

一般情況,像往常一樣定義函式(sumA函式)。在另一種情況下,函式被放置在一對括號中(sumB函式)。

如果呼叫 sumA(1,2)sumB(1,2) 會發生什麼?

如預期的那樣,sumA(1, 2) 返回 3。但是,呼叫sumB(1, 2)會引發異常:Uncaught ReferenceError: sumB is not defined

其原因是sumA是使用函式宣告建立的,該函式宣告在當前作用域中建立一個函式變數(具有與函式名稱相同的名稱)。 但是sumB是使用函式表示式建立的(將其包裝在括號中),該函式表示式不會在當前作用域內建立函式變數。

如果你想訪問使用函式表示式建立的函式,那麼將函式物件儲存到一個變數中:

// Works!
const sum = (function sumB(a, b) {
  return a + b;
});

sum(1, 2); // => 3
如果語句以`function`關鍵字開頭,則為函式宣告,否則為函式表示式。
// 函式宣告:以`function`關鍵字開頭
function sumA(a, b) {
  return a + b;
}

// 函式表示式:不以`function`關鍵字開頭
const mySum = (function sumB(a, b) {
  return a + b;
});

// 函式表示式:不以`function`關鍵字開頭
[1, 2, 3].reduce(function sum3(acc, number) { 
  return acc + number 
});

從更高的角度來看,函式宣告對於建立獨立函式很有用,但是函式表示式可以用作回撥。

現在,我們更深入地研究函式宣告和函式表示式的行為。

2.函式宣告

在前面的示例中已經看到的,sumA是一個函式宣告:

// Function declaration
function sumA(a, b) {
  return a + b;
}

sumA(4, 5); // => 9

當一個語句包含function關鍵字,後跟函式名稱,一對帶引數的括號(param1, param2, paramN)以及包圍在一對花括號{}中的函式主體時,就會發生函式宣告。

函式宣告會建立一個函式變數:一個與函式名稱同名的變數(例如,上一個示例中的sumA)。 在當前作用域中(在函式宣告之前和之後),甚至在函式作用域本身內,都可以訪問該函式變數。

函式變數通常用於呼叫函式或將函式物件傳遞給其他函式(傳遞給高階函式)。

例如,編寫一個函式 sumArray(array),以遞迴方式累加一個陣列的項(該陣列可以包含數字或其他陣列):

sumArray([10, [1, [5]]]); // => 16

function sumArray(array) {
  let sum = 0;
  for (const item of array) {
    sum += Array.isArray(item) ? sumArray(item) : item;
  }
  return sum;
}

sumArray([1, [4, 6]]); // => 11

動手試試:https://jsfiddle.net/dmitri_p...

function sumArray(array) { ... } 是函式宣告。

包含函式物件的函式變數sumArray在當前作用域中可用:sumArray([10, [1, [5]]])之前和sumArray([1, [4, 6]])之後,函式宣告, 以及函式本身的作用域sumArray([1, [4, 6]])(允許遞迴呼叫)。

由於提升,函式變數在函式宣告之前可用。

2.1 函式宣告的注意事項

函式宣告語法的作用是建立獨立函式。 函式宣告應在全域性作用域內,或直接在其他函式的作用域內:

// Good!
function myFunc1(param1, param2) {
  return param1 + param2;
}

function bigFunction(param) {
  // Good!
  function myFunc2(param1, param2) {
    return param1 + param2;
  }

  const result = myFunc2(1, 3);
  return result + param;
}

基於相同的原因,不建議在條件(if)和迴圈(whilefor)中使用函式宣告:

// Bad!
if (myCondition) {
  function myFunction(a, b) {
    return a * b;
  }
} else {
  function myFunction(a, b) {
    return a + b;
  }
}

myFunction(2, 3);

使用函式表示式更好地執行有條件地建立函式。

3.函式表示式

function關鍵字在表示式內部建立函式(帶有或不帶有名稱)時,將出現函式表示式。

以下是使用表示式建立的函式的示例:

// Function expressions

const sum = (function sumB(a, b) {
  return a + b;
});

const myObject = {
  myMethod: function() {
    return 42;
  }
};

const numbers = [4, 1, 6];
numbers.forEach(function callback(number) {
  console.log(number);
  // logs 4
  // logs 1
  // logs 1
});

在函式表示式中建立了兩種函式:

  • 如果表示式中的函式沒有名稱,例如 function(){return 42},那是一個匿名函式表示式
  • 如果函式具有名稱,例如 上一個示例中的sumB和回撥,那麼這是一個命名函式表示式

3.1 函式表示式的注意事項

函式表示式適合作為條件建立的回撥或函式:

// Functions created conditionally
let callback;
if (true) {
  callback = function() { return 42 };
} else {
  callback = function() { return 3.14 };
}

// Functions used as callbacks
[1, 2, 3].map(function increment(number) {
  return number + 1;
}); // => [2, 3, 4]

如果已建立命名函式表示式,請注意,該函式變數僅在建立的函式作用域內可用:

const numbers = [4];
numbers.forEach(function callback(number) {
  console.log(callback); // logs function() { ... }
});

console.log(callback); // ReferenceError: callback is not defined

試一試:https://jsfiddle.net/dmitri_p...

callback是一個命名的函式表示式,因此callback函式變數僅在callback()函式使用域可用,而在外部則不可用。

但是,如果將函式物件儲存到常規變數中,則可以在函式作用域內外從該變數訪問函式物件:

const callback = function(number) {
  console.log(callback); // logs function() { ... }
};

const numbers = [4];
numbers.forEach(callback);
console.log(callback); // logs function() { ... }

試一試:https://jsfiddle.net/dmitri_p...

4. 總結

根據使用function關鍵字建立函式的方式,可以透過兩種方法來建立函式:函式宣告和函式表示式。

留個問題: function sum(a, b) { return a + b } + 1 是函式宣告還是函式表示式,可以在留言中說出你的答案。


編輯中可能存在的bug沒法實時知道,事後為了解決這些bug,花了大量的時間進行log 除錯,這邊順便給大家推薦一個好用的BUG監控工具 Fundebug

原文:https://dmitripavlutin.com/ja...

交流

有夢想,有乾貨,微信搜尋 【大遷世界】 關注這個在凌晨還在刷碗的刷碗智。

本文 GitHub https://github.com/qq44924588... 已收錄,有一線大廠面試完整考點、資料以及我的系列文章。

相關文章