这个 JavaScript 模式叫什么,为什么要使用它?

IT技术 javascript closures iife
2021-01-20 14:19:48

我正在研究THREE.js并注意到一种模式,其中函数定义如下:

var foo = ( function () {
    var bar = new Bar();

    return function ( ) {
        //actual logic using bar from above.
        //return result;
    };
}());

(示例请参见此处的raycast 方法)。

这种方法正常变化如下所示:

var foo = function () {
    var bar = new Bar();

    //actual logic.
    //return result;
};

将第一个版本与正常版本进行比较,第一个版本似乎有所不同:

  1. 它分配自执行函数的结果。
  2. 它在这个函数中定义了一个局部变量。
  3. 它返回包含使用局部变量的逻辑实际函数。

所以主要的区别在于,在第一个变体中,柱形只在初始化时被分配一次,而第二个变体在每次调用时都会创建这个临时变量。

我对为什么使用它的最佳猜测是它限制了 bar 的实例数量(只有一个),从而节省了内存管理开销。

我的问题:

  1. 这个假设正确吗?
  2. 这个图案有名字吗?
  3. 为什么使用这个?
6个回答

你的假设几乎是正确的。让我们先回顾一下。

  1. 它分配一个自执行函数的返回

这称为立即调用函数表达式IIFE

  1. 它在这个函数中定义了一个局部变量

这是在 JavaScript 中拥有私有对象字段的方式,因为它不提供private关键字或功能。

  1. 它返回包含使用局部变量的逻辑的实际函数。

同样,重点是这个局部变量是private

这个图案有名字吗?

AFAIK 你可以称这种模式为Module Pattern引用:

module模式使用闭包封装了“隐私”、状态和组织。它提供了一种包装公共和私有方法和变量的组合的方法,防止部分泄漏到全局范围内并意外地与另一个开发人员的接口发生冲突。使用这种模式,只返回一个公共 API,保持闭包内的所有其他内容都是私有的。

比较这两个示例,我对为什么使用第一个示例的最佳猜测是:

  1. 它正在实现单例设计模式。
  2. 可以使用第一个示例控制创建特定类型对象的方式。与这一点密切相关的一个可能是有效 Java 中描述的静态工厂方法
  3. 如果您每次都需要相同的对象状态,那么它是有效的

但是如果你每次都只需要 vanilla 对象,那么这个模式可能不会增加任何value。

@LukaHorvat :事实上,javascript 并不比其他语言更“强大”(我更喜欢术语表达)。实际上,它的表现力较差,因为保护变量的唯一方法是将它们包含在函数中以避免任何变量重用废话。module模式是编写好的 javascript 代码的明确必要条件,但它不是语言的特性,它更像是一种避免被语言弱点咬住的可悲的解决方法。
2021-03-16 14:19:48
+1 用于正确识别 Addy Osmani 书中的模式。你的命名是正确的 - 这确实是module模式 - 顺便说一下,揭示module模式。
2021-03-25 14:19:48
我同意你的回答,除了“没有现成的私有变量”部分。所有 JS 变量都是词法范围的“开箱即用”,这是一种比“私有”变量(例如在 Java 中发现的)更强大/更通用的机制;因此 JS 支持“私有”变量作为它处理所有变量方式的特例。
2021-04-04 14:19:48
我认为“私有变量”是指私有“对象字段”
2021-04-09 14:19:48

它限制了对象初始化成本,并额外确保所有函数调用使用相同的对象。例如,这允许将状态存储在对象中以供将来调用使用。

虽然它可能确实限制了内存使用,但通常 GC 无论如何都会收集未使用的对象,因此这种模式不太可能有太大帮助。

这种模式是一种特殊的闭包形式

@ChrisHayes 如果它需要一个名字,为什么你需要论证它没有名字?那没有任何意义。如果它需要一个,它一定没有一个。我对模式没有问题,但我认为没有必要将每个简单的习语都归类为模式。这样做会导致以有限的方式思考(“这是一个module模式吗?我是否正确使用了module模式?”与“我有一个带有一些返回函数的局部变量的 IIFE,这个设计对我有用吗?”)
2021-03-16 14:19:48
在 JS 中,它通常被称为“module”
2021-03-27 14:19:48
我不会把它称为“一种特定形式的封闭”,本身。这是一种使用闭包的模式该模式的名称仍在等待中。
2021-04-02 14:19:48
@DaggNabbit 当问题是“这种模式叫什么”时?是的,它需要一个名字,否则就需要一个令人信服的论据来证明它没有名字。此外,模式的存在是有原因的。我不明白你为什么在这里抨击他们。
2021-04-03 14:19:48
真的需要名字吗?一切都必须是模式吗?我们真的需要无穷无尽的“module模式”变体分类法吗?难道它不能只是“带有一些返回函数的局部变量的 IIFE?”
2021-04-06 14:19:48

我不确定这个模式是否有更正确的名称,但对我来说这看起来像是一个module,使用它的原因是为了封装和维护状态。

闭包(由函数内的函数标识)确保内部函数可以访问外部函数中的变量。

在您给出的示例中,foo通过执行外部函数返回(并分配给内部函数,这意味着tmpObject继续存在于闭包中,并且对内部函数的多次调用foo()将对 的同一个实例进行操作tmpObject

您的代码与 Three.js 代码之间的主要区别在于,在 Three.js 代码中,变量tmpObject仅初始化一次,然后在每次调用返回的函数时共享。

这对于在调用之间保持一些状态很有用,类似于static在类 C 语言中使用变量的方式。

tmpObject 是一个私有变量,只对内部函数可见。

它改变了内存使用,但它不是为了节省内存。

我想通过扩展到揭示module模式的概念来为这个有趣的线程做出贡献,这确保所有方法和变量都保持私有,直到它们被显式公开。

在此处输入图片说明

在后一种情况下,加法方法将被称为 Calculator.add();