在javaScript中,每个函数被调用时,都会建立一个新的运行上下文。因为在一个函数里面定义的变量和函数仅仅能在里面訪问。在外面是不行的。上下文提供了一种很是easy的方法来建立私有性。javascript
//makeCounter函数返回另一个匿名函数,这个匿名函数能够訪问到“私有”变量i, 好像有一点“特权”性。 function makeCounter() { // i仅仅能在makeCounter的里面被訪问到 var i = 0; return function() { console.log( ++i ); }; } // 注意'counter'和'counter2'有他们本身的做用域'i' var counter = makeCounter(); counter(); // logs: 1 counter(); // logs: 2 var counter2 = makeCounter(); counter2(); // logs: 1 counter2(); // logs: 2 i; // ReferenceError: i没有被定义(它仅仅存在于makeCounter的内部)
在很是多状况下,你不需要函数返回多个“实例”。你只需要一个单例。又或者在其它状况下,你可能连一个返回值都不需要。html
现在你定义了像function foo(){}或var foo = function(){}这种函数,这样你获得了一个函数的标识符。你可以经过()来调用它。如foo()。java
//像这样定义的函数可以经过后面的()来调用,如foo(),因为foo不过一个 //函数表达式'function(){/*...*/}'的引用 var foo = function(){ /* ... */ } //只能事后面的() ,函数表达式就能被运行,这是不合常理的。 function(){ /* ... */ }(); // SyntaxError: Unexpected token (
正如你以上所看见的,出现了异常。闭包
当JS解释器在全局做用域或函数的里面遇到functionkeyword时,会默以为是一个函数声明,而不是一个函数表达式。假设你不确切地告诉解释器是一个表达式,它会以为是一个没有名字的函数声明,因此致使语法错误异常,因为函数声明是需要名字的。ecmascript
有趣的是,假设你为函数指定了一个名字,并且把括号放在它后面,出于不一样的缘由。解析器也会抛出一个语法错误。当括号放在表达式的的后面意味着表达式是一个将被调用的函数,括号放在声明的后面,会与前面的声明全然分开。只表明一个分组操做符(做为一种控制优先级的一种控制方式)。函数
<span style="font-weight: normal;">//这个函数在语法声明上是有效的。它不过一个声明,紧跟着的括号是无效的, //因为分组操做符需要包含一个表达式 function foo(){ /* ... */ }(); // SyntaxError: Unexpected token ) //现在假设你在括号里放一个表达式,没有异常抛出 //但是函数仍然没有运行 function foo(){ /* ... */ }( 1 ); //事实上上面的至关于下面两个:一个是函数声明,一个是不相关的表达式 function foo(){ /* ... */ } ( 1 )</span>
想要了解不少其它。请訪问, ECMA-262-3 in detail. Chapter 5. Functions性能
幸运的是以上语法错误可以简单地被修复。最普遍的作法是告诉解析器函数表达式不过用括号括起来的,因为在javaScript中,括号不能包含声明。依据这一点,当解析器遇到functionkeyword时,会把它解析成一个表达式而不是函数声明。学习
//下面两种都可以被用来立刻运行一个函数表达式,用函数的运行上下文去建立 //私有性 (function(){ /* ... */ }()); // Crockford推荐使用这个 (function(){ /* ... */ })(); // 但是这一个也工做得很是好 //从括号或强制操做符来看,消除了函数声明和函数表达式的歧义, //当解析器已经预期是一个表达式时,他们也可以被忽略,请看下面的样例 var i = function(){ return 10; }(); true && function(){ /* ...*/ }(); 0, function(){ /* ... */ }(); //假设你不关心函数的返回值或你的代码可读性 //你可以节省一个字节。经过在函数前面加一个一元操做符 !function(){ /* code */ }(); ~function(){ /* code */ }(); -function(){ /* code */ }(); +function(){ /* code */ }(); //还有另外的版本号,但我不清楚它的实现性能会如何,用'new'keyword实现。 //运行是正常的 new function(){ /* code */ } new function(){ /* code */ }()
特别在“消除歧义”的括号(即包着函数表达式的括号),是不需要的(因为解析器已经以为它是表达式了)。对于用在赋值中仍是一个不错的想法。spa
这种括号很是明显暗示了函数表达式可以立刻被调用,并且这个变量将包含函数的返回结果。不是函数自己。这可以免去一些人阅读你代码的麻烦,因为假设你的函数声明很是长时。可能要滚动到如下才知道你这个函数有没有被调用。code
通常来讲。想写出清晰的代码。从技术上说要阻止解析器抛出语法错误异常,编写清晰的代码也需要阻止其它开发人员把错误异常抛向你。
经过传递參数来调用立刻函数。调用具备名称标识的函数并同一时候向它传递參数,这二者的调用方式是很是像的。
不论什么定义在一个函数里面的函数都能訪问它外面函数的所传递进来的參数以及外层函数的变量(这样的关系被称为闭包),一个立刻调用函数表达式可以被用来“锁住”值和保存状态值。
假设你想学习不少其它关于闭包的内容,请阅读Closures explained with JavaScript.
如下例2和例3会依照你指望的运行,立刻运行函数表达式有一个很是大的缺点是它是匿名或没有命名的。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <a href="###">点我</a> <a href="###">点我</a> <a href="###">点我</a> <a href="###">点我</a> <a href="###">点我</a> <script type="text/javascript"> var elems = document.getElementsByTagName( 'a' ); //例1 for ( var i = 0; i < elems.length; i++ ) { console.log(i); elems[ i ].addEventListener( 'click', function(e){ e.preventDefault(); alert( 'I am link #' + i ); }, 'false' ); } //运行结果: //I am link #5 //I am link #5 //I am link #5 //I am link #5 //I am link #5 //例2 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 ); } //运行结果: //I am link #0 //I am link #1 //I am link #2 //I am link #3 //I am link #4 //例3 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' ); } //运行结果: //I am link #0 //I am link #1 //I am link #2 //I am link #3 //I am link #4 </script> </body> </html>