要知道这几种写法之间的区别,我们要先聊些题外话——js中函数的两种命名方式,即表达式和声明式。

  函数的声明式写法为:function foo(){/*...*/},这种写法会导致函数提升,所有function关键字都会被解释器优先编译,不管是声明在什么位置,都可以调用它,但是它本身不会被执行,定义只是让解释器知道其存在,只有在被调用的时候才会执行。

图1 声明式函数

  函数的表达式写法为:var foo=function(){/*...*/},这种写法不会导致函数提升,于是就必须先声明,再调用,否则会出错,如图2。

图2 表达式函数

  现在,回到正题,(function(){}()),(function(){})()这两种是js中立即执行函数的写法,函数表达式后加上()可以被直接调用,但是把整个声明式函数用()包起来的话,则会被编译器认为是函数表达式,从而可以用()来直接调用,如(function foo(){/*...*/})(),但是如果这个括号加在声明式函数后面,如function foo(){/*...*/}(),则会报错,很多博客说这种写法()会被省略,但实际是会出错,因为不符合js的语法,所以想要通过浏览器的语法检查,就必须加点符号,比如()、+、!等,具体可以查看图3。

图3 立即执行函数

总结一下就是:

function foo(){console.log("Hello World!")}()//声明函数后加()会报错
(function foo(){console.log("Hello World!")}())//用括号把整个表达式包起来,正常执行
(function foo(){console.log("Hello World!")})()//用括号把函数包起来,正常执行
!function foo(){console.log("Hello World!")}()//使用!,求反,这里只想通过语法检查。
+function foo(){console.log("Hello World!")}()//使用+,正常执行
-function foo(){console.log("Hello World!")}()//使用-,正常执行
~function foo(){console.log("Hello World!")}()//使用~,正常执行
void function foo(){console.log("Hello World!")}()//使用void,正常执行
new function foo(){console.log("Hello World!")}()//使用new,正常执行

  立即执行函数一般也写成匿名函数,匿名函数写法为function(){/*...*/},就是使用function关键字声明一个函数,但未给函数命名,倘若需要传值,直接将参数写到括号内即可如图4所示。

图4 立即执行函数的传参

  将它赋予一个变量则创建函数表达式,赋予一个事件则成为事件处理程序等。但是需要注意的是匿名函数不能单独使用,否则会js语法报错,至少要用()包裹起来。上面的例子可以写成如下形式:

(function(){console.log("我是匿名函数。")}())
(function(){console.log("我是匿名函数。")})()
!function(){console.log("我是匿名函数。")}()
+function(){console.log("我是匿名函数。")}()
-function(){console.log("我是匿名函数。")}()
~function(){console.log("我是匿名函数。")}()
void function(){console.log("我是匿名函数。")}()
new function(){console.log("我是匿名函数。")}()

  立即执行函数的作用是:1.创建一个独立的作用域,这个作用域里面的变量,外面访问不到,这样就可以避免变量污染。2.闭包和私有数据。提到闭包,不得不提下那道经典的闭包问题。

 <ul id=”test”>
<li>这是第一条</li>
<li>这是第二条</li>
<li>这是第三条</li>
</ul> <script>
var liList=document.getElementsByTagName('li');
for(var i=0;i<liList.length;i++)
{
liList[i].onclick=function(){
console.log(i);
}
};
</script>

  很多人觉得这样的执行效果是点击第一个li,则会输出1,点击第二个li,则会输出二,以此类推。但是真正的执行效果是,不管点击第几个li,都会输出3,如图5所示。因为 i 是贯穿整个作用域的,而不是给每个 li 分配了一个 i,用户触发的onclick事件之前,for循环已经执行结束了,而for循环执行完的时候i=3。

图5 各自点击第1,2,3个li,或是之后再次点了多少次,都会输出3,可见,右边控制台输出了8次3

  但是如果我们用了立即执行函数给每个 li 创造一个独立作用域,就可以改写为下面的这样,这样就能实现点击第几条就能输出几的功能。

 <script>
var liList=document.getElementsByTagName('li');
for(var i=0;i<liList.length;i++)
{
(function(ii) {
liList[ii].onclick=function(){
console.log(ii);
}
})(i)
};
</script>

  在立即执行函数执行的时候,i 的值被赋值给 ii,此后 ii 的值一直不变,如图6所示。i 的值从 0 变化到 3,对应3 个立即执行函数,这 3个立即执行函数里面的 ii 「分别」是 0、1、2。

图6 点击第几个li,就输出几

  其实ES6语法中的let也可以实现上述的功能,仅仅是将for循环中的var换成let,如下所示,有木有觉得很简单明了。

 <script>
var liList=document.getElementsByTagName('li');
for(let i=0;i<liList.length;i++)
{
liList[i].onclick=function(){
console.log(i);
}
}
</script>

  那很多人就觉得用let可以完全取代立即执行函数,到目前为止,可能是我眼界所限制,我所能用到的立即执行函数的确能被let替代,前提是你的运行环境(包括旧的浏览器)支持ES2015。如果不支持,你将不得不求助于以前经典的函数。

详解立即执行函数(function(){}()),(function(){})()的更多相关文章

  1. JS006. 详解自执行函数原理与数据类型的快速转换 (声明语句、表达式、运算符剖析)

    今天的主角: Operator Description 一元正值符 " + "(MDN) 一元运算符, 如果操作数在之前不是number,试图将其转换为number. 圆括号运算符 ...

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

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

  3. 百度地图API详解之事件机制,function“闭包”解决for循环和监听器冲突的问题:

    原文:百度地图API详解之事件机制,function"闭包"解决for循环和监听器冲突的问题: 百度地图API详解之事件机制 2011年07月26日 星期二 下午 04:06 和D ...

  4. ggplot2作图详解:入门函数qplot

    ggplot2作图详解:入门函数qplot   ggplot2的功能不用我们做广告,因为它的作者Hadley Wickham就说ggplot2是一个强大的作图工具,它可以让你不受现有图形类型的限制,创 ...

  5. 立即执行函数与Function

    js立即执行函数: (function ( ){})( ) 与 (function ( ){}( )) 与new Function()区别? new Function() 还是有区别的,fn = ne ...

  6. jQuery的deferred对象详解 jquery回调函数

    http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_of_jquery_deferred_object.html jQuery的 ...

  7. php调用C代码的方法详解和zend_parse_parameters函数详解

    php调用C代码的方法详解 在php程序中需要用到C代码,应该是下面两种情况: 1 已有C代码,在php程序中想直接用 2 由于php的性能问题,需要用C来实现部分功能   针对第一种情况,最合适的方 ...

  8. 实例-sprintf() 函数详解-输出格式转换函数

    Part1:实例 $filterfile = basename(PHP_SELF, '.php'); if (isset($_GET['uselastfilter']) && isse ...

  9. 16、cgminer学习之:popen函数和system函数详解(执行系统命令)

    1.popen函数我们先用man指令查一下popen函数: 函数说明: (1)popen()会调用fork()产生子进程,然后从子进程中调用/bin/sh -c来执行参数command的指令. (2) ...

随机推荐

  1. 为什么StringBuilder是线程不安全的?StringBuffer是线程安全的?

    面试中经常问到的一个问题:StringBuilder和StringBuffer的区别是什么? 我们非常自信的说出:StringBuilder是线程安全的,StirngBuffer是线程不安全的 面试官 ...

  2. Qt疑难问题-模态窗口父类被析构

    最近遇到一个朋友,问了我一个刁钻的问题,当你模态弹出一个窗体时,后台把这个窗体的父类给析构了,这个时候会出现什么样的情况? 听到问题后我真是一脸懵逼呀!从来没有这么写过代码. 随后写了一个简单的测试d ...

  3. 41 (OC)* OC的理解与动态特性

    1:动态类型(Dynamic typing),动态绑定(Dynamic binding)和动态加载(Dynamic loading). 动态类型:即运行时再决定对象的类型.简单说就是id类型.id类型 ...

  4. JsonConvert 转DateTime类型为json 带T

    在调用接口的时候  将Model转换成json Datetime类型多了个T 用的是Newtonsoft.Json.dll 版本v4.5.0.0 代码:paramsjson = JsonConvert ...

  5. [Linux] Linux中重命名文件和文件夹的方法(mv命令和rename命令)

    原文链接 在Linux下重命名文件或目录,可以使用mv命令或rename命令,这里分享下二者的使用方法. mv命令既可以重命名,又可以移动文件或文件夹. 例子:将目录A重命名为B mv A B 例子: ...

  6. seo搜索引擎的优化方法

    现在互联网的入口,一般都是被搜索引擎霸占.所以我们要想让别人搜索时,优先看到我们的网站.有两种方法: 1.竞价排名.这是需要钱的,给的钱越多,排名越靠前.参考某度.. 2.不想花钱,就使用seo搜索引 ...

  7. Java秒杀系统优化的工程要点

    这篇博客是笔者学习慕课网若鱼老师的<Java秒杀系统方案优化 高性能高并发实战>课程的学习笔记.若鱼老师授课循循善诱,讲解由浅入深,欢迎大家支持. 本文记录课程中的注意点,方便以后code ...

  8. 第八届蓝桥杯java b组第五题

    标题:取数位 求1个整数的第k位数字有很多种方法.以下的方法就是一种. 对于题目中的测试数据,应该打印5. 请仔细分析源码,并补充划线部分所缺少的代码. 注意:只提交缺失的代码,不要填写任何已有内容或 ...

  9. execl开发 新接触

    https//www.baidu.com/home/news/data/newspage?nid=3186684148848912913&n_type=0&p_from=1&d ...

  10. Regex分组与Pattern正则表达式对象

    1.正则规则:        1.String  regex  =  "[1-9]"          表示单个字符的取值范围是 1~9,注意是单个字符的取值范围        2 ...