对象/函数/类声明周围的括号是什么意思?

IT技术 javascript syntax
2021-01-28 08:22:07

我是 JavaScript 和YUI 的新手在 YUI 库示例中,您可以找到此构造的许多用途:

(function() {
    var Dom = YAHOO.util.Dom,
    Event = YAHOO.util.Event,
    layout = null,
        ...
})();

我认为最后几个括号是在声明之后执行函数。

... 但是,围绕函数声明的前一组括号呢?

我认为这是一个范围问题;那是将内部变量隐藏到外部函数和可能的全局对象中。是吗?更一般地说,这些括号的机制是什么?

6个回答

它是一个自动执行的匿名函数。第一组括号包含要执行的表达式,第二组括号执行这些表达式。

当试图从父命名空间隐藏变量时,这是一个有用的构造。函数内的所有代码都包含在函数的私有范围内,这意味着它根本无法从函数外部访问,使其成为真正的私有。

看:

http://en.wikipedia.org/wiki/Closure_%28computer_science%29

http://peter.michaux.ca/articles/javascript-namespacing

@Beska:不是真的。JavaScript 是函数范围的(广义上讲),这并不奇怪。并且可以在单个表达式中声明和执行函数。更公平地说,使用它来实现“私有”变量的“黑客”更多是由于 JavaScript 中缺乏成熟的module系统。
2021-03-12 08:22:07
括号仅在此上下文中是必需的,因为function在 JavaScript 中可以是语句或表达式,具体取决于上下文。括号强制它是一个表达式。以下也是有效的:var x = function () { return 1 }()即使周围没有括号。只是一个函数表达式和该表达式的应用(...)在这种情况下,我建议将 a 放在;左括号之前:我编写无分号的代码,并且以 a 开头的行(可能会被解析为从前一行继续的表达式。
2021-03-15 08:22:07
等等...所以这个烂摊子只是为了绕过 JS 使用的奇怪的范围?
2021-03-31 08:22:07
@user166390 => 为什么把“()”放在最后,在“}”之后?
2021-04-05 08:22:07

安迪休姆几乎给出了答案,我只想添加更多细节。

使用此构造,您将创建一个具有自己的评估环境或闭包的匿名函数,然后您立即对其进行评估。这样做的好处是您可以访问匿名函数之前声明的变量,并且您可以在此函数中使用局部变量而不会意外覆盖现有变量。

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功能,因此将有机会获得valueñ即使add_gen函数执行完毕,这就是为什么执行的例子的最后一行时,你会得到5。

借助词法作用域的函数参数,您可以解决在匿名函数中使用循环变量引起的“问题”。举个简单的例子:

for(var i=0; i<5; i++) {
  setTimeout(function(){alert(i)}, 10);
}

“预期”结果可能是从 0 到 4 的数字,但您会得到四个 5 的实例。发生这种情况是因为 setTimeout 和 for 循环中的匿名函数使用了相同的 i变量,所以当函数被评估时,i将是 5。

您可以通过使用问题中的技术以及函数参数在词法范围内获得天真的预期结果。(我在另一个答案中使用了这种方法

for(var i=0; i<5; i++) {
  setTimeout(
     (function(j) {
       return function(){alert(j)};
     })(i), 10);
}

通过对外部函数的立即评估,您将在每次迭代中创建一个名为j的完全独立的变量,并且i的当前值将被复制到该变量中,因此您将获得第一次尝试时天真地预期的结果。

我建议您尝试了解http://ejohn.org/apps/learn/的优秀教程以更好地理解闭包,这是我学到的非常多的地方。

+1确实很棒的答案。
2021-03-15 08:22:07

...但是前一轮围绕所有函数声明的括号呢?

具体来说,它使 JavaScript 将 'function() {...}' 构造解释为内联匿名函数表达式。如果省略括号:

function() {
    alert('hello');
}();

你会得到一个语法错误,因为 JS 解析器会看到 'function' 关键字并假设你正在开始一个形式的函数语句

function doSomething() {
}

...没有函数名就不能有函数语句。

函数表达式和函数语句是两种不同的结构,它们的处理方式非常不同。不幸的是,语法几乎相同,因此不仅让程序员感到困惑,甚至解析器也难以分辨您的意思!

继续跟进安迪休姆和其他人所说的话:

围绕匿名函数的“()”是 ECMA 规范第 11.1.6 节中定义的“分组运算符”:http : //www.ecma-international.org/publications/files/ECMA-ST/Ecma-262 .PDF

从文档中逐字提取:

11.1.6 分组运算符

生产PrimaryExpression : ( Expression ) 的计算方式如下:

  1. 返回评估Expression的结果这可能是参考类型。

在这种情况下,函数被视为表达式。

关于这个问题的一些考虑:

  • 括号:

    浏览器(引擎/解析器)将关键字 function 与

    [optional name]([optional parameters]){...code...}
    

    所以在像 function(){}() 这样的表达式中,最后一个括号没有意义。

    现在想想

    name=function(){} ; name() !?
    

是的,第一对括号强制匿名函数变成一个变量(存储表达式),第二个括号启动评估/执行,所以( function(){} )()是有道理的。

  • 效用: ?

    1. 用于在加载时执行一些代码并将使用的变量与页面的其余部分隔离,尤其是在可能发生名称冲突时;

    2. 将 eval("string") 替换为

      (新函数(“字符串”))()

    3. 为“ =?: ”操作符包装长代码,例如:

      结果 = exp_to_test ? (function(){... long_code ...})() : (function(){...})();

对于 case function name(){}() 变量名是隐式创建的,因此在代码定义之后放置大括号是 name() 的简写;
2021-04-05 08:22:07