試一試
const getRectArea = function (width, height) {
return width * height;
};
console.log(getRectArea(3, 4));
// Expected output: 12
語法
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 中的函式表示式不像函式宣告那樣被提升。你不能在建立函式表示式之前使用它們。
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屬性來遞迴呼叫函式。
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屬性將設定為該名稱,而不是從語法推斷的隱式名稱(例如函式被賦值的變數)。
與宣告不同,函式表示式的名稱是隻讀的。
"use strict";
function foo() {
foo = 1;
}
foo();
console.log(foo); // 1
(function foo() {
foo = 1; // TypeError: Assignment to constant variable.
})();
示例
使用函式表示式
以下示例定義了一個匿名函式並將其賦值給 x。該函式返回其引數的平方。
const x = function (y) {
return y * y;
};
將函式用作回撥
更常見的是它被用作回撥。
button.addEventListener("click", function (event) {
console.log("button is clicked!");
});
使用立即呼叫函式表示式 (IIFE)
IIFE 是一種常見的模式,用於在需要單個表示式的位置,在其自己的作用域中執行任意數量的語句(並可能返回一個值)。許多傳統的 IIFE 用例已被諸如模組和塊作用域宣告等新語法特性所取代。IIFE 本身現在更常使用箭頭函式編寫,但其思想保持不變。通常,IIFE 看起來像這樣。
// 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 模式,這比使用函式宣告或函式表示式更好,因為它確保程式碼只在此處執行一次。
// 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 設計模式》一書。
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 迴圈
在塊作用域的 let 和 const 宣告引入之前,我們可以在一些舊程式碼中看到 IIFE 的以下用法。使用 var 語句,我們只有函式作用域和全域性作用域。假設我們想建立兩個按鈕,文字分別為 Button 0 和 Button 1,當我們點選它們時,我們希望它們分別彈出 0 和 1。以下程式碼不起作用:
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 模式。
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 語句,我們可以簡單地這樣做:
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 等語言構造。
someObject.property = (() => {
switch (someVariable) {
case 0:
return "zero";
case 1:
return "one";
default:
return "unknown";
}
})();
這種方法在您希望將變數宣告為 const 但在初始化期間被迫使用 let 或 var 的情況下特別有用。
let onlyAssignedOnce;
try {
onlyAssignedOnce = someFunctionThatMightThrow();
} catch (e) {
onlyAssignedOnce = null;
}
使用 IIFE,我們可以將變數宣告為 const。
const onlyAssignedOnce = (() => {
try {
return someFunctionThatMightThrow();
} catch (e) {
return null;
}
})();
規範
| 規範 |
|---|
| ECMAScript® 2026 語言規範 # sec-function-definitions |
瀏覽器相容性
載入中…