IIFE:(立即调用函数)
IIFE( 立即调用函数表达式)是一个在定义时就会立即执行的 JavaScript 函数。
IIFE:全称为 Immediately-invoked function expression
其结构为:
(function(){/* code */}());//Crockford recommends this one,括号内的表达式代表函数立即调用表达式
(function(){/* code */})();//But this one works just as well,括号内的表达式代表函数表达式
特点: 立即调用函数中的匿名函数拥有拥有独立的词法作用域,其变量和方法等不能从外部访问。
好处: 使用立即调用函数不仅避免了外界访问此 IIFE 中的变量,而且又不会污染全局作用域,主要的作用是隔离作用域。
对于 IIFE 表达式中圆括号的理解:
在函数声明后加圆括号并不会立即调用,反而会报错,因为解析器会将圆括号默认为是对函数的声明。
function foo(){ /* code */ }();//SyntaxError: Unexpected token
//现在,你把一个表达式放在圆括号里,没有抛出错误...,但是函数也并没有执行,因为:
function foo(){/* code */}(1)
//它等同于如下,一个函数声明跟着一个完全没有关系的表达式:
function foo(){/* code */}
(1);
- 当圆括号出现在匿名函数的末尾想要调用函数时,它会默认将函数当成是函数声明。
- 当圆括号包裹函数时,它会默认将函数作为表达式去解析,而不是函数声明。
这样的括号指明函数表达式将会被立即调用,并且变量将会储存函数的结果,而不是函数本身。当这是一个非常长的函数表达式时,这可以节约比人阅读你代码的时间,不用滚到页面底部去看这个函数是否被调用。
立即调用函数的使用
- 可以保存闭包的状态
- 向外暴露一个全局函数(如 jQuery的封装)
- 立即执行,方便查看函数的调用状态
保存闭包的状态:
看下面例子中,因为 for 循环中 var 声明的变量 i 为全局作用域,循环结束当触发 onclick 事件时 i 保存的是当前最新的值,所以想要触发时显示的都是6.
var liList = ul.getElementsByTagName('li')
for(var i=0; i<6; i++){
liList[i].onclick = function(){
alert(i) // 为什么 alert 出来的总是 6,而不是 0、1、2、3、4、5
}
}
可以使用立即调用函数来隔离作用域,保存每层循环中 i 的值,相当于循环一次就执行一次函数创建了一个新的变量 ii 来保存值。
var liList = ul.getElementsByTagName('li')
for(var i=0; i<6; i++){
function(ii){
liList[ii].onclick = function(){
alert(ii) // 0、1、2、3、4、5
}
}(i)
}
es6 中可以使用 let 来解决这个问题,更加方便,因为 let 声明的变量是在块级作用域中,外部访问不到,相当于每层循环中块级作用域内都有一个变量用来保存 i 的值。let 在 for 循环中具体怎么实现的可以参考以下文章:
- https://segmentfault.com/q/1010000007541743
- https://segmentfault.com/a/1190000014951691?utm_source=tag-newest
向外暴露一个全局函数,可以进行模块化:
如下面例子中,新建一个 myModule.js 实现字母大小写的转换,将方法写在一个立即调用函数中,并向外暴露一个全局对象,方便js模块的管理和使用。
index.html
<script src="myModule.js"></script>
<script>
/**/
myModule.upperCase('bjt')
myModule.lowerCase('BJT')
</script>
myModule.js
;(function () {
function upperCase(msg) {
console.log(msg+'转换为大写字母:'+msg.toUpperCase())
}
function lowerCase(msg) {
console.log(msg+'转换为小写字母:'+msg.toLowerCase())
}
//以对象的形式暴露一个全局变量
window.myModule={
upperCase:upperCase,
lowerCase:lowerCase
}
})()
运行结果:
bjt转换为大写字母:BJT
myModule.js:6 BJT转换为小写字母:bjt
分号的理解
立即调用函数前最好加;分号,避免 js文件合并压缩时代码执行解析出现错误。