首页 文章

解释封装的匿名函数语法

提问于
浏览
341

摘要

你能解释一下JavaScript中封装的匿名函数语法背后的原因吗?为什么这样做: (function(){})(); 但这不是: function(){}();


我所知道的

在JavaScript中,可以创建一个这样的命名函数:

function twoPlusTwo(){
    alert(2 + 2);
}
twoPlusTwo();

您还可以创建匿名函数并将其分配给变量:

var twoPlusTwo = function(){
    alert(2 + 2);
};
twoPlusTwo();

您可以通过创建匿名函数来封装代码块,然后将其包装在括号中并立即执行:

(function(){
    alert(2 + 2);
})();

这在创建模块化脚本时非常有用,可以避免使用可能存在冲突的变量来混淆当前作用域或全局作用域 - 例如Greasemonkey脚本,jQuery插件等 .

现在,我理解为什么会这样 . 括号括起内容并仅公开结果(I 'm sure there'是一种更好的描述方式),例如 (2 + 2) === 4 .


我不明白

但我不明白为什么这也不起作用:

function(){
    alert(2 + 2);
}();

你能解释一下吗?

9 回答

  • 0

    它不起作用,因为它被解析为 FunctionDeclaration ,函数声明的名称标识符是必需的 .

    当用括号括起它时,它被评估为 FunctionExpression ,并且可以命名或不命名函数表达式 .

    FunctionDeclaration 的语法如下所示:

    function Identifier ( FormalParameterListopt ) { FunctionBody }
    

    并且 FunctionExpression

    function Identifieropt ( FormalParameterListopt ) { FunctionBody }
    

    如您所见, FunctionExpression 中的 Identifier (标识符 opt )标记是可选的,因此我们可以使用没有定义名称的函数表达式:

    (function () {
        alert(2 + 2);
    }());
    

    或命名函数表达式:

    (function foo() {
        alert(2 + 2);
    }());
    

    圆括号(正式称为the Grouping Operator)只能包含表达式,并且会计算函数表达式 .

    这两个语法产生可能不明确,它们看起来完全相同,例如:

    function foo () {} // FunctionDeclaration
    
    0,function foo () {} // FunctionExpression
    

    解析器知道它是 FunctionDeclaration 还是 FunctionExpression ,具体取决于它出现的 context .

    在上面的示例中,第二个是表达式,因为Comma operator也可以只处理表达式 .

    另一方面, FunctionDeclaration s实际上只能出现在所谓的“ Program ”代码中,这意味着代码在全局范围之外,并且在其他函数的 FunctionBody 内 .

    应该避免块内的函数,因为它们可能导致不可预测的行为,例如:

    if (true) {
        function foo () { alert('true'); }
    } else {
        function foo () { alert('false!'); }
    }
    
    foo(); // true? false? why?
    

    上面的代码实际上应该产生 SyntaxError ,因为Block只能包含语句(并且ECMAScript规范没有定义任何函数语句),但是大多数实现都是宽容的,并且只需要第二个函数,即警告 'false!' 的函数 .

    Mozilla实现-Rhino,SpiderMonkey, - 具有不同的行为 . 它们的语法包含一个非标准的Function语句,这意味着该函数将在运行时进行评估,而不是在解析时进行评估,因为它与 FunctionDeclaration 一起发生 . 在这些实现中,我们将定义第一个函数 .


    函数可以用不同的方式声明,compare the following

    1-使用分配给变量multiply的Function构造函数定义的函数:

    var multiply = new Function("x", "y", "return x * y;");
    

    2-名为multiply的函数的函数声明:

    function multiply(x, y) {
        return x * y;
    }
    

    3-分配给变量multiply的函数表达式:

    var multiply = function (x, y) {
        return x * y;
    };
    

    4-命名函数表达式func_name,赋值给变量multiply:

    var multiply = function func_name(x, y) {
        return x * y;
    };
    
  • 12

    虽然这是一个古老的问题和答案,但它讨论了一个主题,直到今天仍然引发许多开发人员的循环 . 我可以't count the number of JavaScript developer candidates I'采访过谁不能告诉我函数声明和函数表达式之间的区别,谁也不知道立即调用的函数表达式是什么 .

    但是,我想提一个非常重要的事情,即Premasagar的代码片段即使给了它一个名字标识符也行不通 .

    function someName() {
        alert(2 + 2);
    }();
    

    这不起作用的原因是JavaScript引擎将此解释为函数声明,后跟一个完全不相关的不包含表达式的分组运算符,分组运算符必须包含表达式 . 根据JavaScript,上面的代码片段相当于下面的代码片段 .

    function someName() {
        alert(2 + 2);
    }
    
    ();
    

    我想指出的另一件事可能对某些人有用,就是你为函数表达式提供的任何名称标识符在代码上下文中都是无用的,除非在函数定义本身内 .

    var a = function b() {
        // do something
    };
    a(); // works
    b(); // doesn't work
    
    var c = function d() {
        window.setTimeout(d, 1000); // works
    };
    

    当然,在调试代码时使用带有函数定义的名称标识符总是有用的,但这完全是另一回事...... :-)

  • 6

    已经发布了很好的答案 . 但是我想要注意函数声明返回一个空的完成记录:

    14.1.20 - 运行时语义:Evaluation FunctionDeclaration:function BindingIdentifier(FormalParameters)返回NormalCompletion(空) .

    这个事实并不容易观察,因为尝试获取返回值的大多数方法都会转换函数声明函数表达式 . 但是, eval 显示:

    var r = eval("function f(){}");
    console.log(r); // undefined
    

    调用空完成记录毫无意义 . 这就是 function f(){}() 无法工作的原因 . 实际上JS引擎甚至没有尝试调用它,括号被认为是另一个语句的一部分 .

    但是如果你将函数包装在括号中,它就变成了一个函数表达式:

    var r = eval("(function f(){})");
    console.log(r); // function f(){}
    

    函数表达式返回一个函数对象 . 因此你可以称之为: (function f(){})() .

  • 0

    在javascript中,这称为Immediately-Invoked Function Expression (IIFE) .

    为了使它成为函数表达式,你需要:

    • enclose it using ()

    • place a void operator before it

    • assign it to a variable.

    否则它将被视为函数定义,然后您将无法通过以下方式同时调用/调用它:

    function (arg1) { console.log(arg1) }();
    

    以上将给你错误 . 因为您只能立即调用函数表达式 .

    这可以通过以下两种方式实现:方式1:

    (function(arg1, arg2){
    //some code
    })(var1, var2);
    

    方式2:

    (function(arg1, arg2){
    //some code
    }(var1, var2));
    

    方式3:

    void function(arg1, arg2){
    //some code
    }(var1, var2);
    

    方式4:

    var ll = function (arg1, arg2) {
          console.log(arg1, arg2);
      }(var1, var2);
    

    以上都会立即调用函数表达式 .

  • 44

    我还有一个小小的评论 . 您的代码只需稍加改动即可:

    var x = function(){
        alert(2 + 2);
    }();
    

    我使用上面的语法而不是更广泛的版本:

    var module = (function(){
        alert(2 + 2);
    })();
    

    因为我没有设法让缩进在vim中正确处理javascript文件 . 似乎vim不喜欢开括号内的花括号 .

  • 3

    也许更简短的答案就是那样

    function() { alert( 2 + 2 ); }
    

    是一个定义(匿名)函数的 function literal . 另一个() - pair,被解释为一个表达式,不会出现在顶层,只有文字 .

    (function() { alert( 2 + 2 ); })();
    

    expression statement 中调用匿名函数 .

  • 373
    (function(){
         alert(2 + 2);
     })();
    

    上面是有效的语法,因为在括号内传递的任何内容都被视为函数表达式 .

    function(){
        alert(2 + 2);
    }();
    

    以上是无效的语法 . 因为java脚本语法分析器在函数关键字之后查找函数名称,因为它没有找到任何内容它会引发错误 .

  • 0

    它们可以与参数参数一起使用

    var x = 3; 
    var y = 4;
    
    (function(a,b){alert(a + b)})(x,y)
    

    会导致7

  • 0

    这些额外的括号在全局命名空间和包含代码的匿名函数之间创建了额外的匿名函数 . 并且在其他函数内部声明的Javascript函数中只能访问包含它们的父函数的命名空间 . 由于全局范围与实际代码范围之间存在额外的对象(匿名函数),因此不保留范围 .

相关问题