【问题标题】:Reasonable uses for closure?关闭的合理用途?
【发布时间】:2013-05-17 22:14:56
【问题描述】:

最近,我在公司的生产代码中看到了带有闭包函数的返回。如return (function() {...}()); 和我不喜欢看到这个,但我不是权威来源。我想我会问 StackOverflow 为什么以及何时使用这个好/坏。

* 注意 *

说你不关心命名空间污染,因为所有这些函数都已经关闭了。

示例 1a:

function foo(bar) {
    return {
        x: 1 + bar,
        y: 1 - bar,
        duration: (function() {
            var i = 0,
                len = 5;
            var results = 0;

            for (; i < bar; i++) {
                results += 1 + (results * bar);
            }

            return results;
        }())
    };
}

示例 1b:

function barProcess(bar) {
    var i = 0;
    var len = 5;
    var results = 0;

    for (; i < bar; i++) {
         results += 1 + (results * bar);
    }

    return results;
}

function foo(bar) {
    return {
        x: 1 + bar,
        y: 1 - bar,
        duration: barProcess(bar)
    };
}

示例 1c:

function foo(bar) {
    var i = 0;
    var len = 5;
    var results = 0;

    for (; i < bar; i++) {
         results += 1 + (results * bar);
    }

    return {
        x: 1 + bar,
        y: 1 - bar,
        duration: results
    };
}

观察:

示例 1a:
如果需要,内部函数会利用闭包。

示例 1b:
如果barProcess 可能需要一些闭包,这可能会使参数列表变长并且难以维护。

示例 1c:
每次调用没有额外的函数创建。 它最容易调试(在我看来)。

如果有人能给我一些技术原因,说明为什么应该使用示例 a、b 或 c,那就太好了。


所以,我蹩脚的答案并没有让我满意,所以我尝试了这个。 JS Perf Tests。我认为这让我的观察不会太远。

【问题讨论】:

  • 这个更适合Programmers
  • 在我看来,当你需要保持某个东西的值不变时,你会使用闭包,比如在循环中,否则大多数时候不需要它们。当调用外部函数等没有实际意义时,自执行函数会很方便?
  • 额外说明:当特征检测一个native函数时,只需要决定一次,而不是每次执行,是否可以使用该native函数还是需要使用shim/alternative函数。
  • Example1a 确实在每次执行时都会在内存中创建一个新函数,这似乎很愚蠢。既然值已经在函数本身中被关闭了,为什么还要做第二个函数呢?

标签: javascript closures


【解决方案1】:

我认为不可能为每个简单案例在示例 a、b 和 c 之间选择一个明显的赢家。

在您给出的示例中,版本 (c) 已经足够好,所以我会使用它。也就是说,版本 (a) 将 i、len 和结果变量保持在更紧密的范围内,因此如果您想让“x”和“y”与持续时间“b”更分离,这绝对是一个好方法。

我不喜欢仅仅出于组织或范围的原因将事物拆分为单独的命名函数,就像您在示例 (b) 中所做的那样,因为它往往会使代码更加复杂且难以遵循。但是,如果 barProccess 是一个实际可重用的抽象,您可以为其指定一个清晰的名称,那么将其分开可能是一个好主意。

说你不关心命名空间污染,因为所有这些函数都已经关闭了。

我认为你在这里有点夸张。内部 IFFE 不像外部 IFFE 那样 100% 推荐,但进一步限制范围仍然完全可以,我认为它不值得与你的同事争吵。

【讨论】:

  • 我假设您使用的是 requirejs 之类的东西,所有这些函数都将在 define/require 函数中关闭。
  • 而且版本 a 也慢了 3 倍(通过我提供给 jsperf 的链接)。这是需要注意的。
  • @Michael:优化,需要根据具体情况进行分析。我不确定如果您有一个包含大量函数调用的复杂函数而不是您的小示例(在这种情况下使用版本风格编写代码的麻烦),差异是否仍然相同。
  • 这是一个很好的观点。显然还有更多需要测试!
【解决方案2】:

1a:没有形成闭包,因为外部函数foo() 在内部函数执行时没有返回。内部函数是一个匿名的“自执行”子程序。一个自执行函数可以形成一个闭包,而这个函数既不需要也不需要。这是构建代码的一种稍微“华丽”的方式。

1b:没有形成闭包,因为外部函数foo() 直接调用命名函数barProcess(),它返回一个数值,而不是一个函数。如果还要从代码中的其他地方调用 barProcess(),这种方法会很有用。

1c:没有形成闭包。 foo() 包含一个简单的代码块,它易于理解并且肯定会完成这项工作。由于 javascript for 循环没有自己的范围,foo() 将简化为:

function foo(bar) {
    for(var i=0, results=0; i<bar; i++) {
         results += 1 + (results * bar);
    }
    return {
        x: 1 + bar,
        y: 1 - bar,
        duration: results
    };
}

【讨论】:

  • 我试图不为 c 故意简化(以便人们可以看到所有三个地方的代码都是相同的)
  • 这只是真的重申了我所说的,但没有回答背后的“为什么”。当我说为什么我会这样说:“你为什么吃?”,“因为我饿了”,“不,不,你为什么要吃”,“哦,因为作为一个人,如果我不吃我的身体我会开始消化自己,我会变得困倦。”我正在寻找第二种原因,即更大的原因。
  • 我的主要目的是帮助您理解,在 a/b/c 中,没有一个案例涉及“关闭”——这个词出现在您的问题标题中。下次您想问“为什么”时,请考虑在问题标题中使用该特定疑问句,而不是针对某人善意的回答进行居高临下的演讲。
  • 对不起。我没有意识到,特别是回答 a 不涉及任何形式的关闭。我一定不知道什么是闭包。对不起,我居高临下。
猜你喜欢
  • 2012-09-02
  • 2021-06-29
  • 2010-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-29
相关资源
最近更新 更多