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

Pat*_*lug 100 javascript closures iife

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

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

    return function ( ) {
        //actual logic using bar from above.
        //return result;
    };
}());
Run Code Online (Sandbox Code Playgroud)

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

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

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

    //actual logic.
    //return result;
};
Run Code Online (Sandbox Code Playgroud)

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

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

因此,主要区别在于,在第一个变体中,条形图在初始化时仅分配一次,而第二个变体在每次调用时都创建此临时变量.

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

我的问题:

  1. 这个假设是否正确?
  2. 这个模式有名字吗?
  3. 这是为什么用的?

MD.*_*oob 100

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

  1. 它分配自执行功能的返回

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

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

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

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

同样,重点是这个局部变量是私有的.

这个模式有名字吗?

AFAIK你可以称之为模式模式.引用:

Module模式使用闭包封装了"隐私",状态和组织.它提供了一种包装公共和私有方法和变量的方法,保护组件不会泄漏到全局范围,并意外地与另一个开发人员的界面发生冲突.使用此模式,只返回一个公共API,保持闭包内的其他所有内容.

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

  1. 它正在实施Singleton设计模式.
  2. 可以使用第一个示例控制可以创建特定类型的对象的方式.与此点紧密匹配的可以是Effective Java中描述的静态工厂方法.
  3. 这是有效的,如果你需要在同一个对象的状态每次.

但是如果你每次只需要vanilla对象,那么这个模式可能不会添加任何值.

  • 我同意你的回答,除了"没有开箱即用的私人变量"部分.所有JS变量都是"开箱即用"的词法范围,这是一种比"私有"变量更强大/更通用的机制(例如,如Java中所见); 因此JS支持"私有"变量作为处理所有变量的特殊情况. (4认同)
  • @LukaHorvat:事实上,javascript并不比其他语言更"强大"(我更喜欢术语表达).实际上,它的表达力较差,因为保护变量的唯一方法是将它们包含在函数中,以避免任何变量重用无意义.模块模式是制作优秀javascript代码的必要条件,但它不是该语言的一个特性,它更像是一种令人遗憾的解决方法,可以避免被语言的弱点所困扰. (4认同)

Fen*_*ang 11

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

虽然它可能会限制内存使用,但通常GC会收集未使用的对象,因此这种模式可能不会有太大帮助.

这种模式是一种特定的封闭形式.

  • 它真的需要一个名字吗?一切都必须是一种模式吗?我们真的需要无限的"模块模式"变体分类吗?它不能仅仅是"一个带有一些返回函数的局部变量的IIFE吗?" (4认同)
  • @ChrisHayes如果需要一个名字,为什么你需要做一个没有名字的论点?这没有任何意义.如果它需要一个,它就不能有一个.我对模式没有任何问题,但我认为没有必要将每一个简单的习语分类为模式.这样做会导致以有限的方式思考("这是一个模块模式吗?我是否正确使用模块模式?"与"我有一个带有一些返回函数的局部变量的IIFE,这个设计对我有用吗?") (4认同)
  • @DaggNabbit当问题是"这个模式叫什么"时?是的,它需要一个名称,或者说它没有一个令人信服的论据.此外,模式存在是有原因的.我不明白你为什么要在这里辱骂他们. (3认同)
  • 我本人不会称之为"特定形式的封闭".这是*使用*闭包的模式.模式的名称仍然可以争夺. (2认同)

its*_*die 8

我不确定这个模式是否有更正确的名称,但这对我来说看起来像一个模块,它的用途是封装和维护状态.

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

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


mcf*_*edr 5

代码和Three.js代码之间的关键区别在于,在Three.js代码中,变量tmpObject只初始化一次,然后在返回函数的每次调用时共享.

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

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

它改变了内存使用量,但它不是为了节省内存而设计的.


car*_*rso 5

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

在此处输入图片说明

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