立即执行函数,就是在定义函数的时候直接执行,这里不是申明函数而是一个函数表达式

1.问题

在javascript中,每一个函数在被调用的时候都会创建一个执行上下文,在函数内部定义的变量和函数只能在该函数内部调用,正是因为这个上下文,使得在调用函数的时候可以创建一些私有变量。如下代码

    //makeCounter,返回一个新的函数(闭包),这个函数可以访问makeCounter里的局部变量i
function makeCounter() {
var i = 0;
return function () {
document.write(++i);
document.write('<br>');
}
} //counter1和counter2是不同的实例,分别拥有自己范围内的变量i
var counter1 = makeCounter();
counter1();
counter1(); var counter2 = makeCounter();
counter2();
counter2();

这里i是函数makeCounter函数内的局部变量,所以定义的counter1和counter2都有自己的变量i,上面代码输出结果如下:

注意闭i始终保存在内存中,所以第二次调用的时候输出的是2。

普通情况下我们定义一个函数,然后在语句中函数名字后面加上一对圆括号就可以直接调用它,能不能定义完之后直接在后面加上小括号调用呢?如下

function(){ counter1(); }(); // SyntaxError: Unexpected token (

答案是不行,这样会报错的。为什么呢?在javascript解释代码的时候,遇到function关键字的时候就认为这里是一个函数声明,而不是函数表达式,如果没有显式地定义成函数表达式就会报错,因为函数声明需要一个函数名,上面的代码没有函数名。

既然是因为没有函数名字报错那好就加上一个函数名,如下:

function foo(){ counter1(); }(); // SyntaxError: Unexpected token )

依然会报错,为什么呢?在一个函数声明语句(这次是正确的)后面加上一对圆括号,这对圆括号和前面的声明语句没有任关系,而只是一个分组操作符,用来控制运算的优先级,这里的意思是小括号里面优先计算,所以上面代码等同于:

function foo(){ counter1(); }
(); // SyntaxError: Unexpected token )

2.概念

正确的写法是怎样的呢?简单,如下:

(function () { counter1(); }());

这样为什么就可以呢?在javascript里圆括号内不能包含语句,当解释器对代码进行解释的时候遇到圆括号就认为这里面是表达式,然后遇到function关键字就认为这是一个函数表达式,而不是函数声明。而更加奇妙的是只要是能将后面语句预先解释为表达式都可以,不一定是分分组操作符,于是立即执行函数表达式有了五花八门的写法,如下:

    (function () { counter1(); }());
(function () { counter1(); })();
var i = function(){ counter1(); }();
true && function () { counter1(); }();
0, function(){ counter1() }();
!function () { counter1(); }();
~function () { counter1(); }();
-function () { counter1(); }();
+function () { counter1(); }();

输出结果如下:

甚至可以这样:

    new function(){ counter1(); }
new function(){ counter1(); }() // 带参数

这样:

var i = function(){ counter1(); }();
var j = (function(){ return 10; }());

这是为什么呢?因为new,=是运算符,和+,-,*,/一个样,都会把后面的语句预先解释为表达式。这里推荐上面一种写法,因为function内部代码如果太多,我们不得不滚到最后去看function(){}后是否带有()。

3.立即执行函数和闭包有什么关系

和普通函数传参一样,立即执行函数也可以传递参数。如果在函数内部定一个函数,而里面的那个函数能引用外部的变量和参数(闭包),我们就能用立即执行函数锁定变量保存状态。

我们在hmtl页面中方两个超链接标签,然后用下面的代码来测试:

<div>
<ul>
<li><a>第一个超链接</a></li>
<li><a>第二个超链接</a></li>
</ul>
</div>
var elems = document.getElementsByTagName('a');
for(var i=0; i<elems.length; i++) {
elems[i].addEventListener('click', function (e) {
e.preventDefault();
alert('I am click Link #' + i);
}, 'false')
}

这段代码意图是点击第一个超链接提示“I am click Link #0”,点击第二个提示“I am click Link #1”。真的是这样吗? 不是,每一次都是“I am click Link #2”

因为i的值没有被锁住,当我们点击链接的时候其实for循环早已经执行完了,于是在点击的时候i的值已经是elems.length了。

修改代码如下:

    var elems = document.getElementsByTagName('a');
for(var i=0; i < elems.length; i++){
(function (LockedInIndex) {
elems[i].addEventListener('click', function (e) {
e.preventDefault();
alert('I am cliick Link #' + i);
}, 'false')
})(i)
} 

这次可以正确的输出结果,i的值被传给了LockedIndex,并且被锁定在内存中,尽管for循环之后i的值已经改变,但是立即执行函数内部的LockedIndex的值并不会改变。

还可以这样写:

    var elems = document.getElementsByTagName('a');
for ( var i = 0; i < elems.length; i++ ) {
elems[ i ].addEventListener( 'click', (function( lockedInIndex ){
return function(e){
e.preventDefault();
alert( 'I am link #' + lockedInIndex );
};
})( i ), 'false' );
}

但是我觉得如果用let是不是就可以一下子解决了:

   var elems = document.getElementsByTagName('a');
for(let i=0; i<elems.length; i++) {
elems[i].addEventListener('click', function (e) {
e.preventDefault();
alert('I am click Link #' + i);
}, 'false')
}

let是块级作用域内的变量,是es6新定义的,这里不展开。

4.模块模式

立即执行函数在模块化的时候也有用,用立即执行函数处理模块可以减少全局变量造成的空间污染,而是使用私有变量。

如下创建一个立即执行的匿名函数,该函数返回一个对象,包含要暴露给外部的属性i,如果不实用立即执行函数就要多定义一个属性i了,这个i就会显示的暴露给外部,这样:counter.i,这种方式明显不太安全。

    var counter = (function(){
var i = 0; return {
get: function(){
return i;
},
set: function( val ){
i = val;
},
increment: function() {
return ++i;
}
};
}());
document.write('<br>');
document.write(counter.get());document.write('<br>');
document.write(counter.set( 3 ));document.write('<br>');
document.write(counter.increment());document.write('<br>'); //
document.write(counter.increment());document.write('<br>'); //

注意,这里如果使用counter.i来访问这个内部变量,会报错undefined,因为i并不是counter的属性。

好了,就这么多。

详解javascript立即执行函数表达式(IIFE)的更多相关文章

  1. (译)详解javascript立即执行函数表达式(IIFE)

    写在前面 这是一篇译文,原文:Immediately-Invoked Function Expression (IIFE) 原文是一篇很经典的讲解IIFE的文章,很适合收藏.本文虽然是译文,但是直译的 ...

  2. javascript模块化编程-详解立即执行函数表达式IIFE

    一.IIFE解释 全拼Imdiately Invoked Function Expression,立即执行的函数表达式.  像如下的代码所示,就是一个匿名立即执行函数: (function(windo ...

  3. javascript自执行函数表达式

    解析器在解析function关键字的时候,会将相应的代码解析成function表达式,而不是function声明.// 下面2个括弧()都会立即执行(function () { /* code */ ...

  4. 理解JavaScript的立即调用函数表达式(IIFE)

    首先这是js的一种函数调用写法,叫立即执行函数表达式(IIFE,即immediately-invoked function expression).顾名思义IIFE可以让你的函数立即得到执行(废话). ...

  5. 【JavaScript】浅析IIFE(立即执行函数表达式)的作用

    什么是IIFE IIFE就是立即执行函数表达式(Immediately-Invoked Function Expression) 为什么需要IIFE 应用IIFE有两个比较经典的使用场景, 第一就是在 ...

  6. 立即执行函数表达式(IIFE)

    原文地址:benalman.com/news/2010/11/immediately-invoked-function-expression/ 译者:nzbin 也许你还没有注意到,我是一个对术语比较 ...

  7. [转]Javascript中的自执行函数表达式

    [转]Javascript中的自执行函数表达式 本文转载自:http://www.ghugo.com/javascript-auto-run-function/ 以下是正文: Posted on 20 ...

  8. JS立即执行函数表达式(IIFE)

    原文为 http://benalman.com/news/2010/11/immediately-invoked-function-expression/#iife ----------------- ...

  9. IIFE(立即执行函数表达式)

    我们经常会看到这样的写法: ;(fuction () { // do something })() 这就是一个简单的IIFE(立即执行函数表达式,immediately-invoked functio ...

随机推荐

  1. Android Studio 新建项目结构分析

    这是我刚刚新建的项目  默认都是Android模式的项目结构,但这并不是真实的目录结构 把他换成Project模式 项目的真实目录结构 1app 项目的代码,资源 2 gradle  wrappere ...

  2. 模拟获取post数据的方式

    使用下面两种方法可以获取post数据 .通过$HTTP_RAW_POST_DATA获取 $post=$GLOBALS['HTTP_RAW_POST_DATA']; 但需要修改相应的php.ini指令 ...

  3. log4j介绍

    log4j.properties文件的三种加载方式 1.默认自动加载 满足以下条件时: 1).配置文件名为log4j.properties 2).在classpath根目录下(即resources根目 ...

  4. JS动态创建元素

    动态添加标签,可从服务器获取标签文本,然后加载到指定div中,可用于权限控制. 1.添加标签字符串方式 var str='<div>5555555555555555555555555555 ...

  5. C#时间、日期 的操作

    原文地址:http://zhidao.baidu.com/link?url=q-0No1LWfdyqQKFBg8RN5h0K-X0VygSbFI4sR8lBPwDExkIIzXQivyMKqX7V1y ...

  6. IDEA项目搭建十四——Web站点Controller基类及布局页静态资源设计

    一.简介 站点搭建完成后,编写页面时一般会有如下几个需求 1.嵌套静态页面时有很大一部分通用代码,如css.js这部分可以使用thymeleaf的局部片段代码块组成 2.这些静态资源默认放在程序中,但 ...

  7. 解决在TP5中无法使用快递鸟的即时查询API

    快递鸟的接口对接其实很简单,先去官网注册账号,登陆把基本信息填好,然后在产品管理中订购一下“物流查询”,免费,不过其他产品是收费,免费的有对接口调用频率限制,结合自己的应用流量够用就可以. 使用前复制 ...

  8. iOS ---------- 获取设备的各种信息

    一.目录结构: 获取屏幕宽度与高度 获取设备版本号 获取iPhone名称 获取app版本号 获取电池电量 获取当前系统名称 获取当前系统版本号 获取通用的唯一识别码UUID 获取当前设备IP 获取总内 ...

  9. JMeter 线程组之ConcurrencyThreadGroup介绍

    线程组之ConcurrencyThreadGroup by:授客 QQ:1033553122 测试环境 apache-jmeter-3.2 jmeter-plugins-manager-1.3.jar ...

  10. 不能用notepad++编辑器编写python

    不能用notepad++编辑器编写python,因为notepad对空格支持不是很良好,会出现莫名其妙的错误!建议用vim或emacs. 有人这样解释:不要混合使用制表符和空格来缩进,因为这在跨越不同 ...