代碼對比
// 情況 1:函數調用 + 賦值
function aa() { alert("123"); }
var bb = aa(); // ① 執行 aa() → alert("123") → 返回 undefined → bb = undefined
// 情況 2:函數表達式賦值
var bb = function() { alert("123"); }; // ② 創建匿名函數對象 → 賦值給 bb → 不執行
// 後續調用:bb(); // 才會 alert
本質區別
| 維度 | 情況 1: `var bb = aa()` | 情況 2: `var bb = function(){}` |
| 操作類型 | 函數調用表達式 | 函數表達式 (賦值) |
| 執行時機 | **立即執行** `aa()` | **延遲執行**,賦值時不執行 |
| 結果值 | 函數 `aa` 的**返回值** (`undefined`) | **函數對象本身** (可調用) |
| bb 類型 | `undefined` (或返回值類型) | `Function` |
| 副作用 | 函數體內代碼立即運行 (alert、副作用) | 無副作用,僅創建閉包/對象 |
內存/引擎視角
// 情況 1
1. 解析 aa 函數聲明 → 創建 Function 對象 → 掛載 window.aa
2. 執行 aa() → 壓棧執行上下文 → alert → 彈棧 → 返回 undefined
3. 變量 bb = undefined
// 情況 2
1. 解析 function(){} → 創建 Function 對象 (匿名)
2. 變量 bb = 該 Function 對象引用
3. 結束,函數體未執行
常見應用場景
1. 立即執行函數表達式 (IIFE) - 情況 1 變體
(function() {
var private = 'secret';
window.myModule = { get: () => private };
})(); // 立即執行,創建私有作用域
2. 回調函數傳遞 - 情況 2
setTimeout(function() { console.log('later'); }, 1000); // 傳遞函數對象,稍後調用
btn.addEventListener('click', handleClick); // 傳遞引用,非調用
// ❌ btn.addEventListener('click', handleClick()); // 錯誤:立即執行,傳入 undefined
3. 高階函數返回函數 - 情況 2
function createMultiplier(factor) {
return function(x) { return x * factor; }; // 返回函數對象 (閉包)
}
var double = createMultiplier(2);
double(5); // 10
避坑指南
| 錯誤代碼 | 現象 | 修正 |
| `var x = func();` 期望獲取函數 | x 為返回值 | `var x = func;` (不加括號) |
| `setTimeout(doSomething(), 1000)` | 立即執行 | `setTimeout(doSomething, 1000)` |
| `arr.map(item.process())` | 立即執行 process | `arr.map(item => item.process())` 或 `arr.map(item => item.process)` |
總結
**括號 `()` 是調用運算符**,會立即執行函數體並產生返回值。**無括號只是引用**,傳遞函數對象本身。這是 JavaScript「函數是一等公民」的核心體現:**函數可作為值傳遞、賦值、返回**,只有加 `()` 才觸發執行。牢記:**傳回調用用引用,需要結果才加括號**。