详解javascript立即执行函数表达式IIFE


Posted in Javascript onFebruary 13, 2017

一、IIFE解释

全拼Imdiately Invoked Function Expression,立即执行的函数表达式。

像如下的代码所示,就是一个匿名立即执行函数:

(function(window, undefined){
 // 代码... 
})(window);

二、括号的意义

2.1 包住function(){}的括号的意义

这个括号的目的,是为了把function(){}转化为表达式。像一些库的源码,喜欢用如下方式代替:

~function(){
 // 代码...
}();

或者这种方式:

+function(){
 // 代码...
}();

其实,作用都一样,都是把function(){}转化成一个可执行的表达式,方便执行。

如果去掉该括号,则会报错。因为单纯的function(){}不是可执行的表达式,会直接报错。如下图:

详解javascript立即执行函数表达式IIFE

2.1 第二个括号的意义

理解了第一个括号的意义,第二个括号就很简单了,就是执行表达式了。

三、参数的意义

以这段代码为例子,讲解参数

var wall = {};
(function(window, WALL, undefined){
})(window, wall);

参数分为形参和实参。

function(window, WALL, undefined)三个参数为形参,第二个括号(window, wall)的两个参数为实参。

也即可以理解为 window == window,wall == WALL。

2.1 普通形参

普通形参是指由window和wall这样的实际变量传入指定,可以为任何类型的变量。一个形参就对应一个实参

2.2 特殊形参undefined

为什么形参要多写一个undefined,这是一个很有趣的话题。

可以知道这个示例,实参只有两个,而形参有三个。所以在函数执行的时候,形参undefined会默认赋值为undefined。

形参undefined的作用如下:

2.2.1 防止特殊值undefined被恶意代码篡改。

IE6等低版本浏览器,undefined是支持被修改的。而这个特殊值被修改后,像以下这种判断就失效了。

if(wall == undefined){
 // 代码...
}

所以,这里多加一个形参的目的就是为了防止这种情况发生。只要在这个IIFE作用域内,undefined就能够正常获取到。

2.2.2 压缩代码可以压缩undefined

因为undefined作为形参,像YUI compressor这种类型的代码压缩工具,可以将其相关的值进行压缩,减小文件的体积。

四、写法解析

4.1 普通写法

var wall = {}; // 声明定义一个命名空间wall
// 定义方法
(function(window, WALL, undefined){
 // 给wall命名空间绑定方法say
 WALL.say = function(){
  console.log('hello');
 };
})(window, wall);
(function(window, WALL, undefined){
 // 给wall命名空间绑定方法 whoIam
 WALL.whoIam = function(){
  console.log('wall');
 };
})(window, wall);
// 调用
wall.say();
wall.whoIam();

先定义一个命名空间,然后再给这个命名空间加东西。这是最普遍的写法,也是最好理解的。

不足的地方就是必须先声明一个命名空间,然后才能执行相关的绑定代码。存在顺序加载的问题。

4.2 放大模式

var wall = (function(window, WALL, undefined){
 if(typeof WALL == 'undefined'){
  WALL = {};
 }
 // 给wall命名空间绑定方法say
 WALL.say = function(){
  console.log('hello');
 }
 return WALL; // 返回引用
})(window, wall);
var wall = (function(window, WALL, undefined){
 if(typeof WALL == 'undefined'){
  WALL = {};
 }
 // 给wall命名空间绑定方法 whoIam
 WALL.whoIam = function(){
  console.log('wall');
 }
 return WALL; // 返回引用
})(window, wall);
// 调用
wall.say();
wall.whoIam();

放大模式的好处就是,可以不用考虑代码加载的先后顺序。

因为js允许wall变量进行重复var声明,所以这段代码是可以执行的。

我可以把IIFE函数拆分成多个文件进行加载,而不会出现普通写法需要注意的问题。

需要注意的点:

1.IIFE的头部,都要先进行检查命名空间是否已经实例化,如果还没实例化,则进行实例化。

2.IIFE的尾部,都要return命名空间的引用,使后续代码能够得到最新的wall命名空间内容。

4.3 宽放大模式

(function(window, WALL, undefined){
 // 给wall命名空间绑定方法say
 WALL.say = function(){
  console.log('hello');
 }
})(window, window.wall || (window.wall = {}));
(function(window, WALL, undefined){
 // 给wall命名空间绑定方法 whoIam
 WALL.whoIam = function(){
  console.log('wall');
 }
})(window, window.wall || (window.wall = {}));
// 调用
wall.say();
wall.whoIam();

宽放大模式的重点注意的地方:就是在实参部分的window.wall || (window.wall = {})。

用||运算符进行取巧。

如果window.wall是已经实例化的,非not defined。则直接返回window.wall的引用,赋值给形参WALL。不会执行||运算符后面的内容。

如果window.wall还未实例化,则进行实例化。这里要注意的点是实例化是一个赋值操作,需要用括号包起来,变成表达式去执行,才不会报错。

表达式(window.wall = {})执行完毕后,会返回新对象window.wall的引用。

宽放大模式的好处:是可以切割成多个文件进行加载,而不必考虑文件加载的先后顺序,不存在强耦合关系。

当然,如果IIFE里面的方法互相引用,还是存在加载依赖的问题。这个问题可以用加载器Require.js等工具解决,这里就不讨论了。

五、分文件加载IIFE要注意的点

;(function(window, WALL, undefined){
 // 给wall命名空间绑定方法say
 WALL.say = function(){
  console.log('hello');
 }
})(window, window.wall || (window.wall = {}));

眼尖的已经看出区别了,就是文件开始的地方,先写上分号;。

这样,多个文件合并的时候,才不会出现收尾相接,代码出现错乱的问题。比如下面这种情况:

// a.js 文件
wall.log()
// b.js 文件
(function(window, WALL, undefined){
 // 给wall命名空间绑定方法say
 WALL.say = function(){
  console.log('hello');
 }
})(window, window.wall || (window.wall = {}));

由于a.js文件的wall.log()少写了分号,跟b.js文件合并后,js就会认为‘wall.log()(...)'是需要这么执行的,结果代码就报错了。

觉得不错的,可以关注模块化这个系列的文章,容我后续码字,敬请期待!

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持三水点靠木!

Javascript 相关文章推荐
学习YUI.Ext基础第一天
Mar 10 Javascript
jquery中map函数与each函数的区别实例介绍
Jun 23 Javascript
JS模态窗口返回值兼容问题的完美解决方法
May 28 Javascript
Vuex2.0+Vue2.0构建备忘录应用实践
Nov 30 Javascript
JavaScript 限制文本框不可输入英文单双引号的方法
Dec 20 Javascript
JS图片轮播与索引变色功能实例详解
Jul 06 Javascript
JS实现发送短信验证后按钮倒计时功能(防止刷新倒计时失效)
Jul 07 Javascript
使用vux实现上拉刷新功能遇到的坑
Feb 08 Javascript
vue实现直播间点赞飘心效果的示例代码
Sep 20 Javascript
Openlayers实现地图的基本操作
Sep 28 Javascript
Vue + ts实现轮播插件的示例
Nov 10 Javascript
javascript实现计算器功能详解流程
Nov 01 Javascript
jQuery实现大图轮播
Feb 13 #Javascript
使用ionic在首页新闻中应用到的跑马灯效果的实现方法
Feb 13 #Javascript
你真的了解BOM中的history对象吗
Feb 13 #Javascript
微信小程序-获得用户输入内容
Feb 13 #Javascript
js实现textarea限制输入字数
Feb 13 #Javascript
Canvas实现动态的雪花效果
Feb 13 #Javascript
JavaScript中从setTimeout与setInterval到AJAX异步
Feb 13 #Javascript
You might like
php SQL Injection with MySQL
2011/02/27 PHP
PHP+javascript制作带提示的验证码源码分享
2014/05/28 PHP
配置eAccelerator和XCache扩展来加速PHP程序的执行
2015/12/22 PHP
详解YII关联查询
2016/01/10 PHP
PHP实现的链式队列结构示例
2017/09/15 PHP
Laravel框架分页实现方法分析
2018/06/12 PHP
PHP程序员简单的开展服务治理架构操作详解(一)
2020/05/14 PHP
关于JavaScript的一些看法
2009/05/27 Javascript
jQuery powerFloat万能浮动层下拉层插件使用介绍
2010/12/27 Javascript
.net,js捕捉文本框回车键事件的小例子(兼容多浏览器)
2013/03/11 Javascript
jQuery 绑定事件到动态创建的元素上的方法实例
2013/08/18 Javascript
js淡入淡出的图片轮播效果代码分享
2015/08/24 Javascript
jQuery实现的AJAX简单弹出层效果代码
2015/11/26 Javascript
js实现表单提交后不重新刷新当前页面
2016/11/30 Javascript
移动端利用H5实现压缩图片上传功能
2017/03/29 Javascript
详解webpack运行Babel教程
2018/06/13 Javascript
javascript网页随机点名实现过程解析
2019/10/15 Javascript
解决vue-cli输入命令vue ui没效果的问题
2020/11/17 Javascript
Selenium执行JavaScript脚本的方法示例
2020/12/31 Javascript
Python 正则表达式操作指南
2009/05/04 Python
Python中对元组和列表按条件进行排序的方法示例
2015/11/10 Python
Python里字典的基本用法(包括嵌套字典)
2019/02/27 Python
python微信撤回监测代码
2019/04/29 Python
python多进程(加入进程池)操作常见案例
2019/10/21 Python
使用TensorBoard进行超参数优化的实现
2020/07/06 Python
英国巧克力贸易公司:Chocolate Trading Company
2017/03/21 全球购物
英国品牌男装折扣网站:Brown Bag
2018/03/08 全球购物
公司营业员的工作总结自我评价
2013/10/05 职场文书
说明书格式及范文
2014/05/07 职场文书
要账委托书范本
2014/09/15 职场文书
2014镇副书记群众路线专题民主生活会思想汇报
2014/09/23 职场文书
2014年秘书工作总结
2014/11/25 职场文书
redis连接被拒绝的解决方案
2021/04/12 Redis
Python基础之赋值,浅拷贝,深拷贝的区别
2021/04/30 Python
SQL Server作业失败:无法确定所有者是否有服务器访问权限的解决方法
2021/06/30 SQL Server
MongoDB连接数据库并创建数据等使用方法
2021/11/27 MongoDB