一句话解释什么是立即执行函数?立即执行函数就是声明一个匿名函数,并立即调用这个匿名函数立即执行函数有什么用创建一个独立作用域,这个作用域内的变量不能被外部访问(也就是避免“变量污染”)让我们先问自己一个问题:立即执行函数是闭包吗?如果不能马上回答这个问题,那我们就来看看什么是立即执行函数。MDN的回答是IIFE(立即调用的函数表达式)是一个JavaScript函数,在定义时立即执行)。这样就可以形成一个块级作用域效果(function(){//块级作用域})();这在没有块级作用域的ES3时代很常见。之前有个著名的面试题,像这样:for(vari=0;i<5;i++){setTimeout(function(){console.log(i);},1000*i);}结果是什么,5,5,5,5,5,它每1秒打印一个5问题。有什么方法可以使结果为1、2、3、4、5?下面分析一下为什么一开始就打印5。这是因为setTimeout是异步的,要塞进异步队列,所以一开始先循环,循环结束后再执行setTimeout(func,wait)。所以执行顺序是for(vari=0;i<5;i++){//assignmentsetTimeout(function(){console.log(i)},1000*i)//i1,2,3,4,5}//setTimeout延迟执行,vari统一赋值5setTimeout(function(){console.log(5);},1000*1);setTimeout(function(){console.log(5);},1000*2);setTimeout(function(){console.log(5);},1000*3);setTimeout(function(){console.log(5);},1000*4);setTimeout(函数(){console.log(5);},1000*5);又因为for(){}不会形成块级作用域,所以最后的值,也就是5,会在每个func中给console.log(i)赋值,最终导致这样的打印结果经过分析,我们要考虑如何将变量i保留在setTimeout中。通常的方式是通过作用域来保护它,比如使用块级作用域来保护i。方法是用let代替var。for(leti=0;i<5;i++){//将var改成setTimeout(function(){console.log(i);},1000*i);}或者使用函数作用域保护,因为变量在函数范围内不能在函数外访问for(vari=0;i<5;i++){(function(j){setTimeout(function(){console.log(j);},1000*j);})(i);}使用let的方法的伪代码类似于立即函数。代码理解为:每传入一个变量i,立即执行setTimeout。执行完一次后,for循环中的i变为1,然后执行setTimeout,就达到了效果。为什么原来的面试题会有这样的结果呢?本质是JavaScript中的for循环无法保护i不被改变,即for循环无法形成块级作用域。通过这道题,我们可以清楚的了解到立即执行函数的用处:函数IIFE定义后立即执行的扩展形式。我们常见的IIFE是这样的:;(function(){...})()但是也不乏看到这样的代码(function(window){console.log(window);})(window);一开始我们会很疑惑,这个可以传值吗?让我们看看普通函数是如何工作的。varfoo=function(name){console.log(name);};foo('johan');我们可以在任何地方调用foo函数。创建“IIFE”的原因是因为它们是立即函数表达式,这意味着它们在运行时立即被调用,我们不会再次调用它,它只运行一次,就像这样:varfoo=(function(name){控制台.log(名字);})('johan');甚至,我们不需要分配给foo,因为我们不使用foo(function(name){console.log(name);})('johan');上面的例子很容易理解。有IIFE的原因很简单,定义一个匿名函数,传入参数调用自己,就是为了让一段代码不受其他库的影响执行。在ES6模块出现之前,我们将JavaScript写在HTMLscript标签中,或者写在javascript文件中,然后通过script标签导入。当你写的(全局)方法和别人(不管是第三方库还是同事的代码)一样的时候,就会出现方法覆盖bug。所以使用IIFE可以保证每个IIFE中的代码变量不会在全局范围内被访问,也起到了变量保护的作用适用场景UMDpackaging(function(root,factory){if(typeofdefine==='function'&&define.amd){define(factory);}elseif(typeofexports==='object'){module.exports=factory;}else{root.MYMODULE=factory();}})(this,功能(){//...});本质是把结合了AMD和CommonJS的立即执行函数放在源码里jQuery里:(function(window,undefined){...})(window);在下划线中:(function(global,factory){...}(this,(function(){...})));这些库中使用立即执行函数来保护库中的变量。立即执行函数是闭包吗?回到最初的问题,IIFE是闭包吗??肯定不是,IIFE是立即执行函数,执行完就会被垃圾回收。怎么可能是闭包呢?什么是闭包?闭包就是利用作用域机制来控制私有变量。两者为何混淆?因为IIFE可以起到隔离变量的作用,所以在出来之前是一种hack的模块化变量保护机制。闭包也可以起到隔离变量的作用。所以两者会混淆。是否存在立即执行函数实现闭包的场景?varModule=(function(){varprivate='privatevariable';varfoo=function(){console.log(private);};return{foo:foo,};})();Module.foo();//私有变量Module.private;//undefined立即执行函数不是闭包,但是可以做出闭包效果第三题见实章第一题(function(){if(typeofname==='undefined'){console.log('Goodbye'+name);}else{varname='Jack';console.log('Hello'+name);}})(); 解题思路: 我们都知道函数定义立即执行就是函数的立即执行。既然是函数,就形成了作用域。本题在函数中有变量提升,即在函数顶部提到了varname,默认是undefined,所以当typeofname==='undefined'时,console.log('Goodbyeundefined')answer
Goodbyeundefined
1
4
3
1
报错this.fn5notfunction
第三题varliList=ul.getElementsByTagName('li');for(vari=0;i<6;i++){liList[i].onClick=function(){alert(i);//为什么alert总是6,而不是0,1,2,3,4,5};Answer
为什么alert的值总是6,因为我遍历整个scope而不是给每个li赋一个i解决方案有很多,比如用let代替var或利用IIFES