function 表示式

Baseline 已廣泛支援

此特性已相當成熟,可在許多裝置和瀏覽器版本上使用。自 ⁨2015 年 7 月⁩以來,各瀏覽器均已提供此特性。

function 關鍵字可用於在表示式中定義函式。

你也可以使用function 宣告箭頭語法來定義函式。

試一試

const getRectArea = function (width, height) {
  return width * height;
};

console.log(getRectArea(3, 4));
// Expected output: 12

語法

js
function (param0) {
  statements
}
function (param0, param1) {
  statements
}
function (param0, param1, /* …, */ paramN) {
  statements
}

function name(param0) {
  statements
}
function name(param0, param1) {
  statements
}
function name(param0, param1, /* …, */ paramN) {
  statements
}

注意:表示式語句不能以關鍵字 function 開頭,以避免與function 宣告產生歧義。只有當 function 關鍵字出現在不能接受語句的上下文中時,它才開始一個表示式。

引數

name 可選

函式名稱。可以省略,這種情況下函式是匿名的。該名稱僅在函式體內部區域性有效。

paramN 可選

函式形式引數的名稱。有關引數的語法,請參閱函式參考

statements 可選

構成函式體的語句。

描述

function 表示式與function 宣告非常相似,並且具有幾乎相同的語法。function 表示式和 function 宣告之間的主要區別是函式名稱,在 function 表示式中可以省略該名稱以建立匿名函式。function 表示式可以用作IIFE(立即呼叫函式表示式),它在定義後立即執行。有關更多資訊,請參閱關於函式的章節。

函式表示式提升

JavaScript 中的函式表示式不像函式宣告那樣被提升。你不能在建立函式表示式之前使用它們。

js
console.log(notHoisted); // undefined
// Even though the variable name is hoisted,
// the definition isn't. so it's undefined.
notHoisted(); // TypeError: notHoisted is not a function

var notHoisted = function () {
  console.log("bar");
};

命名函式表示式

如果你想在函式體內部引用當前函式,你需要建立一個命名函式表示式。這個名稱只在函式體(作用域)內部區域性有效。這避免了使用已棄用的arguments.callee屬性來遞迴呼叫函式。

js
const math = {
  factorial: function factorial(n) {
    console.log(n);
    if (n <= 1) {
      return 1;
    }
    return n * factorial(n - 1);
  },
};

math.factorial(3); // 3;2;1;

如果一個函式表示式被命名,函式的name屬性將設定為該名稱,而不是從語法推斷的隱式名稱(例如函式被賦值的變數)。

與宣告不同,函式表示式的名稱是隻讀的。

js
"use strict";

function foo() {
  foo = 1;
}
foo();
console.log(foo); // 1
(function foo() {
  foo = 1; // TypeError: Assignment to constant variable.
})();

示例

使用函式表示式

以下示例定義了一個匿名函式並將其賦值給 x。該函式返回其引數的平方。

js
const x = function (y) {
  return y * y;
};

將函式用作回撥

更常見的是它被用作回撥

js
button.addEventListener("click", function (event) {
  console.log("button is clicked!");
});

使用立即呼叫函式表示式 (IIFE)

IIFE 是一種常見的模式,用於在需要單個表示式的位置,在其自己的作用域中執行任意數量的語句(並可能返回一個值)。許多傳統的 IIFE 用例已被諸如模組塊作用域宣告等新語法特性所取代。IIFE 本身現在更常使用箭頭函式編寫,但其思想保持不變。通常,IIFE 看起來像這樣。

js
// standard IIFE
(function () {
  // statements…
})();

// IIFE with arguments
(function (a, b) {
  console.log(a + b);
})(1, 2); // logs 3

// IIFE being used to initialize a variable
const value = (() => {
  const randomValue = Math.random();
  if (randomValue > 0.5) {
    return "heads";
  }
  return "tails";
})();

在此,我們將介紹幾個帶有示例的用例。

避免在指令碼程式碼中汙染全域性名稱空間

所有指令碼的頂級作用域都是共享的,這可能包含來自不同檔案的許多函式和全域性變數,因此為了避免名稱衝突,限制全域性宣告的名稱數量很重要(這在模組中得到了很大緩解,但有時限制臨時變數的作用域仍然很有用,特別是當檔案非常長時)。如果我們有一些不需要再次使用的初始化程式碼,我們可以使用 IIFE 模式,這比使用函式宣告或函式表示式更好,因為它確保程式碼只在此處執行一次。

js
// top-level of a script (not a module)

var globalVariable = (() => {
  // some initialization code
  let firstVariable = something();
  let secondVariable = somethingElse();
  return firstVariable + secondVariable;
})();

// firstVariable and secondVariable cannot be accessed outside of the function body.

模組模式

我們還會使用 IIFE 來建立私有和公共變數以及方法。有關模組模式的更復雜用法和 IIFE 的其他用法,你可以查閱 Addy Osmani 的《學習 JavaScript 設計模式》一書。

js
const makeWithdraw = (balance) =>
  ((copyBalance) => {
    let balance = copyBalance; // This variable is private
    const doBadThings = () => {
      console.log("I will do bad things with your money");
    };
    doBadThings();
    return {
      withdraw(amount) {
        if (balance >= amount) {
          balance -= amount;
          return balance;
        }
        return "Insufficient money";
      },
    };
  })(balance);

const firstAccount = makeWithdraw(100); // "I will do bad things with your money"
console.log(firstAccount.balance); // undefined
console.log(firstAccount.withdraw(20)); // 80
console.log(firstAccount.withdraw(30)); // 50
console.log(firstAccount.doBadThings); // undefined; this method is private
const secondAccount = makeWithdraw(20); // "I will do bad things with your money"
console.log(secondAccount.withdraw(30)); // "Insufficient money"
console.log(secondAccount.withdraw(20)); // 0

ES6 之前的 var 迴圈

在塊作用域的 letconst 宣告引入之前,我們可以在一些舊程式碼中看到 IIFE 的以下用法。使用 var 語句,我們只有函式作用域和全域性作用域。假設我們想建立兩個按鈕,文字分別為 Button 0 和 Button 1,當我們點選它們時,我們希望它們分別彈出 0 和 1。以下程式碼不起作用:

js
for (var i = 0; i < 2; i++) {
  const button = document.createElement("button");
  button.innerText = `Button ${i}`;
  button.onclick = function () {
    console.log(i);
  };
  document.body.appendChild(button);
}
console.log(i); // 2

當點選時,Button 0 和 Button 1 都彈出 2,因為 i 是全域性的,其最終值為 2。為了在 ES6 之前解決這個問題,我們可以使用 IIFE 模式。

js
for (var i = 0; i < 2; i++) {
  const button = document.createElement("button");
  button.innerText = `Button ${i}`;
  button.onclick = (function (copyOfI) {
    return function () {
      console.log(copyOfI);
    };
  })(i);
  document.body.appendChild(button);
}
console.log(i); // 2

點選時,Button 0 和 Button 1 分別彈出 0 和 1。變數 i 是全域性定義的。使用 let 語句,我們可以簡單地這樣做:

js
for (let i = 0; i < 2; i++) {
  const button = document.createElement("button");
  button.innerText = `Button ${i}`;
  button.onclick = function () {
    console.log(i);
  };
  document.body.appendChild(button);
}
console.log(i); // Uncaught ReferenceError: i is not defined.

點選時,這些按鈕分別彈出 0 和 1。

表示式位置中的控制流語句

IIFE 使我們能夠在表示式中使用諸如 switch 等語言構造。

js
someObject.property = (() => {
  switch (someVariable) {
    case 0:
      return "zero";
    case 1:
      return "one";
    default:
      return "unknown";
  }
})();

這種方法在您希望將變數宣告為 const 但在初始化期間被迫使用 letvar 的情況下特別有用。

js
let onlyAssignedOnce;
try {
  onlyAssignedOnce = someFunctionThatMightThrow();
} catch (e) {
  onlyAssignedOnce = null;
}

使用 IIFE,我們可以將變數宣告為 const

js
const onlyAssignedOnce = (() => {
  try {
    return someFunctionThatMightThrow();
  } catch (e) {
    return null;
  }
})();

規範

規範
ECMAScript® 2026 語言規範
# sec-function-definitions

瀏覽器相容性

另見