我是JavaScript和YUI的新手。在YUI库示例中,您可以找到此构造的许多用法:
(function() {
var Dom = YAHOO.util.Dom,
Event = YAHOO.util.Event,
layout = null,
...
})();
我认为最后几个括号是在声明之后执行该函数。
...但是围绕函数声明的前一组括号呢?
我认为这是一个范围问题;这是将内部变量隐藏到外部函数和可能的全局对象中。是吗?更一般地说,这些括号的机制是什么?
答案 0 :(得分:211)
这是一个自动执行的匿名函数。第一组括号包含要执行的表达式,第二组括号执行这些表达式。
尝试隐藏父命名空间中的变量时,它是一个有用的构造。函数中的所有代码都包含在函数的私有范围内,这意味着它无法从函数外部访问,从而使其真正成为私有。
请参阅:
答案 1 :(得分:138)
Andy Hume几乎gave答案,我只是想补充一些细节。
使用此构造,您将创建具有自己的评估环境或闭包的匿名函数,然后立即对其进行评估。关于这一点的好处是你可以访问在匿名函数之前声明的变量,你可以在这个函数中使用局部变量,而不会意外地覆盖现有变量。
使用 var 关键字非常重要,因为在JavaScript中,默认情况下每个变量都是全局变量,但是使用关键字创建一个新的词法范围变量,也就是说,两个大括号之间的代码可以看到它。在您的示例中,您实际上是在YUI库中为对象创建短别名,但它具有更强大的用途。
我不想在没有代码示例的情况下离开你,所以我将在这里举一个简单的例子来说明一个闭包:
var add_gen = function(n) {
return function(x) {
return n + x;
};
};
var add2 = add_gen(2);
add2(3); // result is 5
这里发生了什么?在函数 add_gen 中,您正在创建另一个函数,只需将 n 数字添加到其参数中。诀窍在于,在函数参数列表中定义的变量充当词法范围的变量,就像用 var 定义的变量一样。
返回的函数在 add_gen 函数的大括号之间定义,因此即使在add_gen函数之后它也可以访问 n 的值已完成执行,这就是为什么在执行示例的最后一行时会得到5。
借助于词法范围的函数参数,您可以解决在匿名函数中使用循环变量引起的“问题”。举一个简单的例子:
for(var i=0; i<5; i++) {
setTimeout(function(){alert(i)}, 10);
}
“预期”结果可能是从零到四的数字,但是你会得到四个五的实例。发生这种情况是因为setTimeout和for循环中的匿名函数使用的是非常相同的 i 变量,因此在评估函数时, i 将是5。
您可以通过在您的问题中使用该技术获得天真预期的结果,并且事实上,函数参数是词法范围的。 (我在other answer)
中使用了这种方法for(var i=0; i<5; i++) {
setTimeout(
(function(j) {
return function(){alert(j)};
})(i), 10);
}
通过立即评估外部函数,您将在每次迭代中创建一个名为 j 的完全独立的变量,并且 i 的当前值将被复制进入此变量,因此您将获得第一次尝试时天真期望的结果。
我建议您尝试理解http://ejohn.org/apps/learn/上的优秀教程,以更好地理解闭包,这是我非常了解的地方。
答案 2 :(得分:46)
...但是围绕所有功能声明的前一轮问题呢?
具体来说,它使JavaScript将'function(){...}'构造解释为内联匿名函数表达式。如果省略括号:
function() {
alert('hello');
}();
您会收到语法错误,因为JS解析器会看到'function'关键字,并假设您正在启动表单的函数语句:
function doSomething() {
}
...如果没有函数名,就不能有函数语句。
函数表达式和函数语句是两种不同的结构,它们以非常不同的方式处理。不幸的是,语法几乎完全相同,所以它不仅让程序员感到困惑,甚至解析器也很难说出你的意思!
答案 3 :(得分:14)
突然跟进Andy Hume和其他人所说的话:
围绕匿名函数的'()'是ECMA规范第11.1.6节中定义的'分组运算符':http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf。
从文档中逐字逐句:
11.1.6分组运算符
生产 PrimaryExpression :( 表达式)评估如下:
在此上下文中,该函数被视为表达式。
答案 4 :(得分:6)
关于这个问题的一些考虑因素:
括号:
浏览器(引擎/解析器)将关键字函数与
相关联[optional name]([optional parameters]){...code...}
所以在像function(){}()这样的表达式中,最后一个括号没有意义。
现在想想
name=function(){} ; name() !?
是的,第一对括号强制匿名函数变成变量(存储表达式),第二对启动评估/执行,所以( function(){} )( )是有道理的。
实用程序:?
用于在加载时执行某些代码并将使用过的变量与页面的其余部分隔离开,尤其是在可能存在名称冲突时;
用
替换eval(“string”)(new Function(“string”))()
为“ = ?: ”运算符包含长代码,如:
result = exp_to_test? (function(){... long_code ...})():( function(){...})();
答案 5 :(得分:4)
如果愿意,第一个括号用于操作顺序。围绕函数定义的括号集的“结果”是函数本身,实际上是第二组括号执行。
至于为什么它有用,我还不够了解任何想法的JavaScript向导。 :P
答案 6 :(得分:1)
见this question。如果使用函数名称,则第一组括号不是必需的,但是无名函数需要此构造,并且括号用于编码人员在浏览代码时意识到他们正在查看自调用函数(请参阅一个博主的{ {3}})。