JavaScript 函式單元 – IIFE(立即執行函式)
簡介
在 JavaScript 的開發過程中,我們常常需要 建立一次性的執行環境,或是 避免全域變數汙染。
IIFE(Immediately Invoked Function Expression,立即執行函式)正是為了這類需求而誕生的技巧。它能在定義的同時立即執行,並將裡面的變數、函式封裝在自己的作用域裡,從而達到 模組化、資料隱私 以及 一次性初始化 的目的。
對於剛踏入前端開發的同學來說,IIFE 可能看起來有點陌生;但在實務專案、第三方函式庫(例如 jQuery)以及測試框架中,IIFE 已經是不可或缺的基礎結構。掌握它不僅能寫出更安全、可維護的程式碼,也為日後學習 ES6+ 模組系統打下堅實基礎。
以下文章將從概念說明、語法變化、常見陷阱、最佳實踐,一路帶你深入了解 IIFE,並提供多個實務範例,讓你立即在專案中上手。
核心概念
什麼是 IIFE?
IIFE 本質上是一個 函式表達式(Function Expression),它在宣告完畢後會立刻被呼叫。語法上通常會把函式包在圓括號 ( ) 中,接著緊接著一組呼叫括號 ():
(function () {
// 這裡的程式碼會立即執行
})();
- 第一組括號
()讓 JavaScript 將function(){}視為 表達式 而非 宣告(Function Declaration)。 - 第二組括號
()才是真正的 呼叫,此時函式立即執行。
為什麼要使用 IIFE?
- 建立私有作用域
變數不會洩漏到全域,避免變數衝突。 - 一次性初始化
如設定全域設定、綁定事件、執行啟動程式碼等。 - 模組化的前身
在 ES6import/export出現之前,IIFE 是最常見的模組化手法。
基本語法變形
| 變形 | 語法範例 | 說明 |
|---|---|---|
| 傳統寫法 | (function(){ /* ... */ })(); |
最常見、最具可讀性的寫法。 |
| 箭頭函式 | (() => { /* ... */ })(); |
ES6+ 語法,簡潔但仍保留立即執行特性。 |
| 帶參數 | (function(a, b){ console.log(a + b); })(1, 2); |
可以在 IIFE 呼叫時傳入外部變數。 |
使用 !、+、~ 等運算子 |
!function(){ /* ... */ }(); |
透過其他運算子將函式轉為表達式,較少使用但同樣有效。 |
程式碼範例
1. 基本的 IIFE(封裝變數)
(function () {
// 只在此作用域內可見
const secret = 'I am hidden';
console.log('IIFE 執行中,secret =', secret);
})();
// console.log(secret); // ReferenceError: secret is not defined
重點:
secret只存在於 IIFE 內部,外部無法直接存取。
2. 帶參數的 IIFE(傳入全域變數)
const globalName = 'Alice';
(function (name) {
console.log(`Hello, ${name}!`); // Hello, Alice!
})(globalName);
使用 IIFE 可以安全地傳入全域變數,而不必在函式內直接引用全域名稱,提升可測試性。
3. 使用箭頭函式的 IIFE(簡潔寫法)
(() => {
const now = new Date();
console.log('現在時間:', now.toISOString());
})();
箭頭函式不會產生自己的
this、arguments,在需要 純粹執行程式碼 時非常合適。
4. 嵌套 IIFE(模擬私有模組)
const Counter = (function () {
// 私有變數
let count = 0;
// 回傳公開介面
return {
inc() {
count++;
console.log('count =', count);
},
reset() {
count = 0;
console.log('count 已重置');
}
};
})();
Counter.inc(); // count = 1
Counter.inc(); // count = 2
Counter.reset(); // count 已重置
// console.log(Counter.count); // undefined
這是一個 簡易的模組模式:
count被完全封閉在 IIFE 內部,外部只能透過inc、reset操作。
5. 立即執行的匿名函式搭配 async/await
(async () => {
const data = await fetch('https://api.example.com/items')
.then(res => res.json());
console.log('取得的資料長度:', data.length);
})();
在需要 非同步初始化 時,使用
async IIFE能讓程式碼保持線性、易讀。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方式 |
|---|---|---|
| 誤把 IIFE 當成 Function Declaration | 若省略外層括號,function(){} 會被視為宣告,導致語法錯誤或不會立即執行。 |
確保外層有 ( )、!、+ 等運算子包住函式。 |
this 失效 |
在普通函式 IIFE 中,this 會指向全域(strict mode 為 undefined),若預期是物件本身會出錯。 |
使用 箭頭函式(不綁定 this)或在 IIFE 內手動 bind。 |
| 過度使用 IIFE | 把所有程式碼都包在 IIFE 內會降低可讀性,且在 ES6+ 模組已普及的情況下,使用過度會顯得過時。 | 僅在需要私有作用域或一次性初始化時使用;模組化則優先使用 export/import。 |
| 變數命名衝突 | 若 IIFE 內部變數名稱與外部同名,仍會產生混淆。 | 盡量使用 語意化、具體的名稱,或直接傳入參數避免直接引用外部變數。 |
| 未處理例外 | IIFE 內部發生錯誤會直接拋到全域,可能導致程式中斷。 | 在 IIFE 內加入 try...catch 或使用 async IIFE 並捕獲 Promise 錯誤。 |
最佳實踐
- 只在需要封閉作用域時使用:如插件初始化、一次性設定等。
- 以參數傳入外部依賴:
(function (window, document){ ... })(window, document);能讓壓縮工具更有效率。 - 保持簡潔:若僅有一兩行程式碼,直接寫成普通程式碼即可,避免過度包裝。
- 結合 ES6 模組:在現代專案中,將 IIFE 放在模組內部,作為 私有輔助函式 使用。
- 使用嚴格模式:在 IIFE 最前面加上
'use strict';,確保變數不會意外成為全域。
(function (global) {
'use strict';
// 你的程式碼...
})(window);
實際應用場景
| 場景 | 為何適合使用 IIFE |
|---|---|
| 第三方函式庫的封裝 | 如 jQuery、Lodash 等,都以 IIFE 包住整個庫,避免全域衝突。 |
| 一次性設定或初始化 | 設定全域設定、註冊事件監聽、啟動 SPA 路由等,只需執行一次。 |
| 模組化舊版瀏覽器 | 在不支援 ES6 模組的環境,使用 IIFE 建立類似 namespace 的結構。 |
| 測試環境的 Mock | 在測試檔案中,用 IIFE 包住臨時的全域變數或函式,測試結束後自動釋放。 |
| 非同步初始化 | 如前例的 async IIFE,在頁面載入時立即呼叫 API,取得資料後再渲染。 |
範例:在一個簡易的插件中,我們常會看到這樣的寫法:
(function (global, $) {
// 插件代碼
const pluginName = 'myPlugin';
function init() {
console.log(`${pluginName} 初始化`);
// 其他設定...
}
// 暴露給全域
global[pluginName] = {
init
};
})(window, jQuery);
// 使用方式
myPlugin.init();
此寫法同時 隔離 $(jQuery) 以及 保護全域命名空間,即使在多個插件同時載入時,也不會互相干擾。
總結
IIFE(立即執行函式)是 JavaScript 中一個簡潔而強大的工具,它允許開發者:
- 建立私有作用域,防止全域汙染。
- 一次性執行初始化程式碼,如設定、事件綁定或非同步資料取得。
- 模擬模組化,在 ES6 之前提供了封裝與公開介面的方式。
在實務開發中,適度使用 IIFE 能提升程式碼的可維護性與安全性;但也要注意不要過度包裝,尤其在現代專案已支援 ES6+ 模組的情況下,應優先考慮 import/export。
掌握 IIFE 後,你將能更自如地控制執行環境、避免變數衝突,並在舊版瀏覽器或第三方函式庫中游刃有餘。祝你在 JavaScript 的旅程中寫出更乾淨、更可靠的程式碼!