关于括号的重要笔记
在一些情况下,当额外的带着歧义的括号围绕在函数表达式周围是没有必要的(因为这时候的括号已经将其作为一个表达式去表达),但当括号用于调用函数表达式时,这仍然是一个好主意。
这样的括号指明函数表达式将会被立即调用,并且变量将会储存函数的结果,而不是函数本身。当这是一个非常长的函数表达式时,这可以节约比人阅读你代码的时间,不用滚到页面底部去看这个函数是否被调用。
作为规则,当你书写清楚明晰的代码时,有必要阻止 JavaScript 抛出错误的,同样也有必要阻止其他开发者对你抛出错误 WTFError!
保存闭包的状态
就像当函数通过他们的名字被调用时,参数会被传递,而当函数表达式被立即调用时,参数也会被传递。一个立即调用的函数表达式可以用来锁定值并且有效的保存此时的状态,因为任何定义在一个函数内的函数都可以使用外面函数传递进来的参数和变量(这种关系被叫做闭包)。
// 它的运行原理可能并不像你想的那样,因为`i`的值从来没有被锁定。 // 相反的,每个链接,当被点击时(循环已经被很好的执行完毕),因此会弹出所有元素的总数, // 因为这是 `i` 此时的真实值。 var elems = document.getElementsByTagName('a'); for(var i = 0;i < elems.length; i++ ) { elems[i].addEventListener('click',function(e){ e.preventDefault(); alert('I am link #' + i) },false); } // 而像下面这样改写,便可以了,因为在IIFE里,`i`值被锁定在了`lockedInIndex`里。 // 在循环结束执行时,尽管`i`值的数值是所有元素的总和,但每一次函数表达式被调用时, // IIFE 里的 `lockedInIndex` 值都是`i`传给它的值,所以当链接被点击时,正确的值被弹出。 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 link #' + lockedInIndex); },false) })(i); } //你同样可以像下面这样使用IIFE,仅仅只用括号包括点击处理函数,并不包含整个`addEventListener`。 //无论用哪种方式,这两个例子都可以用IIFE将值锁定,不过我发现前面一个例子更可读 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); }
记住,在这最后两个例子里,lockedInIndex可以没有任何问题的访问i,但是作为函数的参数使用一个不同的命名标识符可以使概念更加容易的被解释。
立即执行函数一个最显著的优势是就算它没有命名或者说是匿名,函数表达式也可以在没有使用标识符的情况下被立即调用,一个闭包也可以在没有当前变量污染的情况下被使用。
自执行匿名函数(“Self-executing anonymous function”)有什么问题呢?
你看到它已经被提到好几次了,但是它仍然不是那么清楚的被解释,我提议将术语改成"Immediately-Invoked Function Expression",或者,IIFE,如果你喜欢缩写的话。
什么是Immediately-Invoked Function Expression呢?它使一个被立即调用的函数表达式。就像引导你去调用的函数表达式。
我想Javascript社区的成员应该可以在他们的文章里或者陈述里接受术语,Immediately-Invoked Function Expression和 IIFE,因为我感觉这样更容易让这个概念被理解,并且术语"self-executing anonymous function"真的也不够精确。
//下面是个自执行函数,递归的调用自己本身 function foo(){foo();}; //这是一个自执行匿名函数。因为它没有标识符,它必须是使用`arguments.callee`属性来调用它自己 var foo = function(){arguments.callee();}; //这也许算是一个自执行匿名函数,但是仅仅当`foo`标识符作为它的引用时,如果你将它换成用`foo`来调用同样可行 var foo = function(){foo();}; //有些人像这样叫'self-executing anonymous function'下面的函数,即使它不是自执行的,因为它并没有调用它自己。然后,它只是被立即调用了而已。 (function(){ /*code*/ }()); //为函数表达式增加标识符(也就是说创造一个命名函数)对我们的调试会有很大帮助。一旦命名,函数将不再匿名。 (function foo(){/* code */}()); //IIFEs同样也可以自执行,尽管,也许他不是最有用的模式 (function(){arguments.callee();}()) (function foo(){foo();}()) // One last thing to note: this will cause an error in BlackBerry 5, because // inside a named function expression, that name is undefined. Awesome, huh? (function foo(){ foo(); }());