IIFE(立即调用函数表达式)
IIFE(立即调用函数表达式)是一个在定义时就会立即执行的 JavaScript 函数。IIFE 这个名字是由 Ben Alman 在他的博客中提出的。
(function () {
// …
})();
(() => {
// …
})();
(async () => {
// …
})();
它是一种设计模式,也被称为自执行匿名函数,主要包含两部分:
- 第一部分是一个具有词法作用域的匿名函数,并且用圆括号运算符
()
运算符闭合起来。这样不但阻止了外界访问 IIFE 中的变量,而且不会污染全局作用域。 - 第二部分创建了一个立即执行函数表达式
()
,通过它,JavaScript 引擎将立即执行该函数。
示例
避免污染全局命名空间
因为我们的程序可能包括很多来自不同源文件的函数和全局变量,因此限制全局变量的数量非常重要。如果我们有一些不再使用的初始化代码,我们可以使用 IIFE 模式。由于我们不会再次重用代码,因此在这种情况下使用 IIFE 会比使用函数声明或者函数表达式更好。
(() => {
// 初始化代码
let firstVariable;
let secondVariable;
})()
// firstVariable 和 secondVariable 变量在函数执行后会被丢弃
执行一个异步函数
模块模式
我们也可以使用 IIFE 来创建私有和公有变量、方法。对于模块模式的更复杂用法和 IIFE 的其他用途,你可以参考 Addy Osmani 的 《Learning JavaScript Design Patterns》这本书。
const makeWithdraw = (balance) => {
((copyBalance) => {
let balance = copyBalance; // 这个变量是私有的
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 之前在 For 循环中使用 var
在 ES6 引入 let 和 const 声明和块级作用域之前,我们可以在一些旧代码中看到 IIFE 的以下用法。通过 var 声明变量,只有函数作用域和全局作用域。假设我们创建两个按钮,文本按钮 0 和按钮 1,并且当我们点击它们时,想让它们 alert 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
当点击时,按钮 0 和按钮 1 都会 alert 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 () => {
console.log(copyOfI);
};
})(i);
document.body.appendChild(button);
}
console.log(i); // 2
当点击时,按钮 0 和按钮 1 会 alert 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.
当我们点击按钮时,会 alert 0 和 1。