JavaScript匿名、具名函数与立即执行函数IIFE详解

来源:互联网 发布:ubuntu jdk 1.8 编辑:程序博客网 时间:2024/05/21 09:37

JavaScript中的函数作用域的存在就是为了把变量和函数“隐藏”起来
符合我们的最小特权原则
同时它的另一个好处是可以避免同名标识符的冲突
今天主要来谈谈这个立即执行函数
在此之前的复习一下匿名函数与具名函数

匿名函数与具名函数

无论是匿名还是具名,都是针对函数表达式来说的
函数声明那就必须得有名字了,否则会报错的

function foo(){ //函数声明    //...}

这里我解释一个彩蛋,可能大家看我写文章的时候示例代码总是喜欢用一些
fn、func、demo、foo、bar、foobar之类的词
fn、func就是function“函数”的缩写
demo就是单词demonstration“示例”的缩写
foo、bar、foobar我们经常能够在技术书籍还有计算机文献中看到
foo是fu的变体,fuck-up缩写,意思是“一团糟”
bar是“beyond all recognition”,意思是“超越认知”,通俗说“识别不了,一塌糊涂”
就是一些占位词,相当于我们的小明小红、甲乙丙丁、张三李四…

函数表达式是可以有名字,也可以没有名字的

var foo = function(){...};console.log(foo.name); //foovar bar = function foobar(){...};//不要这么写console.log(bar.name); //foobar

可以看到第二种写法只是单纯地改变了函数的name属性
除此之外百无一用,我们不要这么写

除此之外我们最常见的用法就是函数表达式作为回调参数了
比如说在定时器中

setTimeout(function(){    //...},1000);

这里我写了匿名函数表达式
它用起来简单粗暴
但有几个缺点

  • 追踪栈中没函数名,调试困难
  • 如果需要引用自身,只能用arguments.callee(ES5严格模式禁用)
  • 降低了函数可读性、可理解性

我们在解除事件绑定时,也需要函数名(如果不是用onclick等等直接绑定的话)
所以
给函数表达式一个名字是一个最佳实践,是一个好习惯

setTimeout(function timerHandler(){    //...},1000);

立即执行函数常用用法

立即执行函数,我习惯这么叫
也有叫自执行函数、自动执行函数什么的
说的更标准一些
立即执行函数表达式
注意我的用词,是函数表达式

它并不是什么语法
而是大家用着用着发现还能这么用
于是就流传开来,直到现在
顾名思义,就是执行流运行到这个函数就立刻执行了
社区给它规定了术语:IIFE(Immediately Invoked Function Expression)

我们常用的用法是:

(function(){    //...}());
(function(){    //...})();

我们用IIFE的时候一般都是使用一个匿名函数表达式
当然你加上名字也不错,同时拥有了具名函数表达式的优点
两种用法完全等价,使用哪种由你决定
但我更愿意把括号写在外面,感觉看着比较舒服
而且看到很多大神也和我一样

立即执行函数进阶用法

稍微进阶的用法就是在立即执行函数中传递参数
比如说我们常见的传递window

var a = '全局';(function IIFE(global){    var a = '局部';    console.log(a);// "局部"    console.log(global.a);// "全局"})(window);

传window有什么好处呢?
从我们作用域的角度分析
(可以看看我写的作用域->传送门)
全局对象的引用缓存到了局部环境
这样我们可以更快的访问到全局对象,不用再跳到顶级作用域查找
这点小优化不是重点
重点的压缩代码的时候可以进行优化
也许上面的代码压缩工具可以压缩成这样

var a = '全局';(function IIFE(g){    var a = '局部';    console.log(a);// "局部"    console.log(g.a);// "全局"})(window);

当代码非常大的时候,可不要小看这优化

立即执行函数变化用法

IIFE还有一种变式,你一定见过

(function IIFE(demo){    demo(window);})(function demo(global){    //...});

这种把要运行的函数放进参数的模式看起来很麻烦
但却被广泛使用,jQuery等框架整体架构也和这个很像
如果看习惯了,就会感觉它比我们传统的使用方法更容易理解

整体的函数表达式定义在IIFE第二部分,作为参数传递进IIFE第一部分
然后调用了这个函数,并且把window作为参数传入

立即执行函数的理解

立即执行函数为什么能够自动执行?

(function(){    //...})();

为什么function(){}()这样写不可以
因为这是函数声明,函数声明是不可以执行的
只有表达式才能够执行

var demo = function(){    console.log(1);// 1    return 123;}();console.log(demo);// 123

这就是一个表达式,所以可以被执行
demo得到的是函数的返回值
既然表达式可以被执行,那么以下方式都可以被立即执行

+function(){    console.log(1);// 1}();-function(){    console.log(2);// 2}();!function(){    console.log(3);// 3}(),function(){    console.log(4);// 4}();

可以看出甚至是逗号,都可以它们变成函数表达式,然后立刻执行
不过我们千万不要这么写
如果函数有返回值的话,可能会产生意想不到的副作用
并且可读性也不好

总结

  • 函数声明必须有名字
  • 具名函数表达式是一个最佳实践
  • 立即执行函数表达式用法:(function(){}()); (function(){})();
  • 立即执行函数传递window参数优化在作用域链查找window速度,有利于压缩代码
  • 只有表达式才能够被执行

==主页传送门==

4 0