【问题标题】:Understanding anonymous JavaScript function definition with regard to closures了解关于闭包的匿名 JavaScript 函数定义
【发布时间】:2016-07-30 07:08:11
【问题描述】:

我在概念上对闭包有很好的处理,但是出现了一个我不太理解的问题。

当创建一个函数以将某个值传递给内部函数而不将其绑定到最外层函数返回时的最终值时,这似乎是不允许的:

function buildList(list) {
    var result = [];
    for (var i = 0; i < list.length; i++) {
            function (x) {  // this anonymous function definition...
                var item = 'item' + list[x];
                result.push(
                    return function () {
                        console.log(item + ' ' + list[x]);
                    };
                );
            }(i);           // and simultaneous invocation...
    }
    return result;
}

而如果我将闭包完全移到对 .push() 的调用中,一切正常:

function buildList(list) {
    var result = [];
    for (var i = 0; i < list.length; i++) {
            result.push(
                function (x) {  // wrapper now inside the call to .push()
                    var item = 'item' + list[x];
                    return function () {
                        console.log(item + ' ' + list[x]);
                    };
                }(i)           // and called here...
            );
    }
    return result;
}

我想知道的是:当我定义将闭包立即包装在 for 循环内而不是在调用 .push() 内的匿名函数时,我违反了什么规则?

【问题讨论】:

  • result.push( return function... 永远不会起作用,与关闭无关; return 是一个语句,语句是无效的。所以即使它没有抛出语法,你也不会 push() 什么。你也不能在 push() 中有一个结尾半,所以第一个代码有一些问题......
  • 这似乎不允许”是什么意思?
  • 定义“效果很好”。您的预期行为是什么?每种情况有什么不同?
  • @VivinPaliath 是的。我正在建立一个功能列表。在第二个示例中实现了预期的行为。在第一个示例中,它中断了。我假设是因为我错误地定义了闭包。
  • @dandavis Dang。你说得对。但是,从广义上讲,我可以在 for 循环的每次迭代中创建一个匿名函数作为闭包的最外层包装器(并同时调用它),对吗?这没有明显的问题吗?

标签: javascript closures anonymous-function


【解决方案1】:

通过“不允许”,我假设解释器在抱怨语法错误。在第一种情况下你有什么:

result.push(
    return function () {
        console.log(item + ' ' + list[x]);
    };
);

在语法上无效。

但是即使你去掉返回:

function buildList(list) {
    var result = [];
    for (var i = 0; i < list.length; i++) {
            function (x) {  // this anonymous function definition...
                var item = 'item' + list[x];
                result.push(
                    function () {
                        console.log(item + ' ' + list[x]);
                    }
                );
            }(i);           // and simultaneous invocation...
    }
}

您仍然会收到错误消息。这是因为您没有为 IIFE 指定括号,这意味着 function (x) { ... }() 被视为声明/语句,无论尾随 () 是什么。但是如果你要声明一个函数,你需要指定一个名字,这就是为什么function之后的(是出乎意料的。如果要将其视为表达式,则必须将其包装在 (...) 中,因此使用 (function (x) { ... })()

在第二种情况下,result.push(...) 的参数只能是一个表达式,因此对于如何解释 function (x) { ... }() 没有歧义;它永远不能是一个声明,所以它必须是一个表达式(函数文字或 IIFE)。

作为类比,将function() { ... } 视为字符串"hello"。你永远不能单独使用"hello";以下代码在语法上无效:

var x = "foo";
"hello";

这本质上就是您使用匿名函数所做的事情:

var x = "foo";
function () {
}

该功能应该做什么?它没有分配给任何东西,就像前面示例中的"hello" 没有分配给任何东西一样。但是我们知道函数可以被调用,所以我们对(function() { ... } ()) 所做的就是说“使用我在这里定义的这个函数,然后立即调用它”。这类似于在字符串文字上调用方法:

"abcd".substring(0, 2); // returns "ab"

事实上,你可以用一个函数做类似的事情,我认为这更好地展示了 IIFE 正在发生的事情:

// logs "hello"
(function() { 
    console.log("hello");
}).call();

括号是一种消除歧义并告诉解释器您要将函数视为表达式/文字而不是声明的方法。在上面的示例中,如果您删除了周围的括号,您将收到关于意外( 的相同语法错误。

【讨论】:

  • 啊。这很棒。非常感谢您的清晰解释。
  • @WilliamM。很高兴为您提供帮助!
【解决方案2】:

包装器 (IIFE) 函数存在语法错误,并且在第一种情况下错误地放置了 return 语句。这里有一个固定的(并且稍微修改过的)sn-p。

function buildList(list) {
    var result = [];
    for (var i = 0; i < list.length; i++) {
            (function (x) {  // this anonymous function declaration...
                var item = 'item' + list[x];
                result.push(
                    function () {
                        console.log(item + ' ' + list[x]);
                        return item + ' ' + list[x];
                    }
                );
            })(i);           // and simultaneous invocation...
    }
    return result;
}

buildList([1,2,3]).forEach(function(func) { 
  
  document.body.innerHTML += func() + '<br>';

});

【讨论】:

    【解决方案3】:

    在第一种情况下,您正在推动函数声明 NOT 调用的返回,这实际上是

    result.push(return function(){...})

    在第二种情况下,您正在推动 IIFE 函数执行的返回,它返回您的原始函数,因此它有效

    result.push(function(){...})

    显然,第二个就是你想要的。你可以先改成

    result.push(
        function () {
            console.log(item + ' ' + list[x]);
        };
    );
    

    让它工作。推送时没有return

    【讨论】:

      猜你喜欢
      • 2017-01-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-08
      • 2014-02-20
      相关资源
      最近更新 更多