1、什么是立即调用函数?它有什么特点?
Immediate Invoke Function Expression MDN 上的释义是 “一个在定义时就会立即执行的 JavaScript 函数”
示例:
;(function () {
statements
})()
这是一个被称为 自执行匿名函数 的设计模式,主要包含两部分:
- 第一部分是包围在 圆括号运算符 () 里的一个匿名函数,这个匿名函数拥有独立的词法作用域。这不仅避免了外界访问此 IIFE 中的变量,而且又不会污染全局作用域;
- 第二部分再一次使用 () 创建了一个立即执行函数表达式。
了解了立即调用函数的定义之后,他的特点我们也可以总结出来。
- 定义时就会立即执行;
- 函数内部拥有独立的作用域,外界无法访问 IIFE 中的变量。
2、IIFE 有哪些写法
为什么要将匿名函数放在括号中?拿出来不可以吗?我们来试一试。
function(){
console.log(123)
}() // err: Uncaught SyntaxError: Function statements require a function name
加上函数名称
function foo(){
console.log(123)
}() // err: Uncaught SyntaxError: Unexpected token ')'
又出现了另一个错误
两种方式都出现了语法错误,我们用 AST 解析工具来看一下,上面两种 写法都被识别为了 FunctionDeclaration + ExpressionStatement
正确的写法识别为了 FunctionExpression CallExpression
产生语法错误的原因是,function 这个关键字,既可以当做声明语句,也可以当做表达式
//语句
function fn() {}
//表达式
var fn = function () {}
为了避免歧义,JS 引擎规定,如果 function 出现在行首,一律解析成声明语句。所以上面两种写法的错误分别是
- 声明函数时缺少函数名;
- 分组操作符() 中缺少表达式。
了解了上述问题,我们就可以写出很多种 IIFE 的写法了。
// 常用写法
;(function () {
console.log(123)
})()
;(function () {
console.log(123)
})()
void (function () {
console.log(123)()
})()
// 带返回值和参数
var result = (function (param) {
console.log(param)
return param + 1
})(123)
// 使用箭头函数的 IIFE
;(() => {
console.log(123)
})()
总之要让编译器将function 识别为表达式而不是函数声明。
3、常见使用场景
3.1、隔离作用域
IIFE 最常见的功能,就是隔离作用域,在 ES6 之前 JS 原生也没有块级作用域的概念,所以需要函数作用域来模拟。像一些常见的库 jquery vue 代码就是在 IIFE 中执行的。
优点如下。
- 避免全局变量的污染:IIFE 可以创建一个新的作用域,防止代码中的变量污染全局作用域,避免了命名冲突和变量重复定义的问题。
- 模块化开发:IIFE 可以模拟模块化开发,将代码拆分成独立的模块,使得代码更易于管理和维护。
- 提高代码的可读性:IIFE 可以将代码逻辑封装在函数中,让代码更加清晰,易于理解和阅读。
- 代码安全性更高:IIFE 可以保护代码的私有性,防止恶意代码或攻击者对代码的非法访问或篡改。
3.2 用闭包保存状态
如for循环中
for (var i = 0; i < 5; i++) {
;(function (index) {
setTimeout(function () {
console.log(index)
}, 1000)
})(i)
}
// 0
// 1
// 2
// 3
// 4
3.3 惰性函数
函数被调用时,仅仅进行一次函数定义的判断,而不是每次都进行判断,从而优化了代码性能。加强了代码可读性。
var addEvent = (function () {
if (typeof window.addEventListener === 'function') {
return function (element, type, handler) {
element.addEventListener(type, handler, false)
}
} else if (typeof document.attachEvent === 'function') {
return function (element, type, handler) {
element.attachEvent('on' + type, handler)
}
} else {
return function (element, type, handler) {
element['on' + type] = handler
}
}
})()