专栏名称: 前端大全
分享 Web 前端相关的技术文章、工具资源、精选课程、热点资讯
目录
相关文章推荐
前端早读课  ·  【早说】业务核对清单 ·  1 周前  
前端早读课  ·  【早阅】JS中的OOPS - 终极指南 ·  1 周前  
前端大全  ·  吊打 NVM 的 4 款 Node.js ... ·  1 周前  
前端早读课  ·  【早阅】Tauri采用指南 ·  1 周前  
前端早读课  ·  【早阅】仅使用CSS和Popover ... ·  1 周前  
51好读  ›  专栏  ›  前端大全

JavaScript 立即执行函数表达式 ( IIFE ) 用例

前端大全  · 公众号  · 前端  · 2017-09-04 19:00

正文

(点击上方公众号,可快速关注)

英文:Marius Schulz   译文:众成翻译

www.zcfy.cc/article/use-cases-for-javascript-x27-s-iifes-marius-schulz-4082.html

如有好文章投稿,请点击 → 这里了解详情


最近,我写了一篇关于syntax of JavaScript’s IIFE pattern的文章,来解释为什么我们用现在的方式来写立即执行函数表达式。少数的读者批评文章过时了,都在争论在ECMAScript 2015中介绍的块级作用域变量使IIFE变得过时了。


恰恰相反,立即执行函数表达式一点也没有过时!因为这个理由,我决定写这篇后续文章来介绍一些立即执行函数表达式的常见用法。注意以下的列表是不完整的,所以如果你喜欢的用法没有在文章出现,希望你不要有什么不好的感觉。


函数作用域 VS 块级作用域


通过var关键字声明的本地变量仅作用于当前闭包域,如果不存在这样的一个闭包函数,那么将会创建一个污染全局作用域的全局变量。为了防止这种情况出现,我们可以使用IIFE来创建一个包含有这个本地变量的函数。


(function() {

    var foo = "bar";

    console.log(foo);

})();

 

foo; // ReferenceError: foo is not defined


目前的争论是,我们可以使用在ECMAScript 2015介绍的块级作用域变量来代替IIFE,以达到相同的效果。相比于函数级作用域,let和const关键字声明的本地变量仅作用于当前所处的”块”级域。


{

    let foo = "bar";

    console.log(foo);

}

 

foo; // ReferenceError: foo is not defined


然而,块级作用域变量不是立即函数执行表达式的替代品。确实,如果支持ECMAScript 2015,let和const能够用来限制本地变量只在包含它的块级作用域内使用。


如果,你在不支持ECMAScript 2015的环境(例如一些旧的浏览器)中执行你的JavaScript代码。你就不能使用let和const关键字来创建块级作用域变量。你将不得不求助于以前经典的函数级作用域方法。


闭包和私有数据


IIFE的另一个用法是为局部变量提供一个封装的作用域,在IIFE返回的函数中能够访问该变量。这种方式即_a closure is created_允许函数访问这个本地变量,即使这个函数在IIFE的词法范围之外执行时。


假设我们要创建一个uniqueId函数,每次调用该函数时就会返回一个唯一的id(比如 “id_1”,“id_2”等)。在下面的IIFE中,记录了一个私有的计数变量(count),每次调用计数函数uniqueId的时候,就会将count加一。我们在IIFE中返回的另一个函数,这个函数在调用时会返回一个新的标识符字符串。


const uniqueId = (function() {

    let count = 0;

    return function() {

        ++count;

        return id_${count};

    };

})();

 

console.log(uniqueId()); // "id_1"

console.log(uniqueId()); // "id_2"

console.log(uniqueId()); // "id_3"


注意,在IIEF之外无法访问这个计数变量count。除了从IIEF中返回的函数,别人无法读写该变量。这样就能创建真正的私有状态,它只能以受控的方式进行修改。revealing module pattern非常依赖于这种机制。


const counter = (function() {

    let counterValue = 0;  

 

    return {

        increment() {

            ++counterValue;  

        },  

 

        get value() {

            return counterValue;

        }

    };

})();

 

counter.increment();

console.log(counter.value); // 1

 

counter.increment();

counter.increment();

console.log(counter.value); // 3


当使用IIFE来返回一个”封闭”一些本地变量来管理私有数据的函数时,let和const都不能替代它。


变量重命名


有时,你可能碰到一种情况,你正在使用的两个不同的库暴露的全局变量名是相同的。例如,考虑一下你正在使用jQuery同时另一个库也指定了一个为$的全局变量。


为了解决命名冲突问题,可以将一段代码封装在一个IIEF中,将一个全局变量(比如,jQuery)作为参数传入IIFE。在函数内部,就可以以一个任意的参数名(比如,$)来访问该参数值:


window.$ = function somethingElse() {

    // ...

};

 

(function($) {

    // ...

})(jQuery);


不管在外部作用域有什么值指定给$,在IIFE中,这些值都会被”屏蔽”,$参数一直指向jQuery方法。


捕获全局对象


JavaScript代码在不同环境执行时,你所使用的全局对象是不同的。当代码在浏览器运行时,全局对象是windows。但是在Node.js中,全局对象是global。由于在写通用的JavaScript代码时,你肯定不想硬编码这两个名字其中的任何一个,这时你就可以使用一种”包装”的方式就像下面这样:


(function(global) {

    // ...

})(this);


不管是浏览器还是Node.js的环境,global参数将会指定到对的全局对象上。如果想了解更多关于使用这种技巧来捕获全局对象的细节内容,请移步this post by Todd Motto。


压缩方面的优化


混叠变量名的方法也可以用来优化代码,这种方式使代码能够被更有效的压缩。举例如下:


(function(window, document, undefined) {

    // ...

})(window, document);


一个JavaScript压缩工具例如UglifyJS可以缩短函数的参数名为单个字母的标识符


(function(w, d, u) {

    // ...

})(window, document);


更短标识符名会使文件的体积变得更小。然而,如果HTTP的返回内容通过Gzip或者Deflate进行压缩,文件的大小已经被很有效的压缩了。因此,如果结合压缩算法,压缩技术的边际收益会变得更小。所以自己权衡和比较返回内容的大小,较短的名字可能仍然是有作用的。



觉得本文对你有帮助?请分享给更多人

关注「前端大全」,提升前端技能