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

时间:2014-09-29 03:27:13

标签: javascript closures iife

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

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

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

(例子见raycast方法here)。

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

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

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

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

  1. 它分配自执行功能的结果。
  2. 它在此函数中定义了一个局部变量。
  3. 返回实际函数,其中包含使用局部变量的逻辑。
  4. 因此,主要区别在于,在第一个变体中,条形图在初始化时仅分配一次,而第二个变体在每次调用时都会创建此临时变量。

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

    我的问题:

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

6 个答案:

答案 0 :(得分:100)

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

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

这称为Immediately-invoked function expressionIIFE

  
      
  1. 它定义了此函数中的局部变量
  2.   

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

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

同样,重点是该局部变量是私有

  

这个模式有名字吗?

AFAIK您可以调用此模式Module Pattern。引用:

  

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

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

  1. 正在实施Singleton设计模式。
  2. 可以使用第一个示例控制可以创建特定类型的对象的方式。如有效Java中所述,与此点的一个紧密匹配可以是static factory methods
  3. 如果每次都需要相同的对象状态,则为efficient
  4. 但是如果你每次只需要vanilla对象,那么这个模式可能不会添加任何值。

答案 1 :(得分:11)

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

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

此模式是closure的特定形式。

答案 2 :(得分:8)

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

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

在您给出的示例中,通过执行外部函数返回内部函数(并分配给foo),这意味着tmpObject继续存在于闭包内并且多次调用内部函数{ {1}}将在foo()

的同一个实例上运行

答案 3 :(得分:5)

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

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

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

它会更改内存使用量,但不是为了节省内存。

答案 4 :(得分:4)

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

enter image description here

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

答案 5 :(得分:-1)

在提供的示例中,第一个代码段将对函数foo()的每次调用使用相同的tmpObject实例,其中与第二个代码段一样,tmpObject每次都是一个新实例。

可能已经使用了第一个代码段的一个原因是变量tmpObject可以在调用foo()之间共享,而不会将其值泄漏到声明foo()的范围内。

第一个片段的非立即执行的功能版本实际上如下所示:

var tmpObject = new Bar();

function foo(){
    // Use tmpObject.
}

但请注意,此版本的tmpObject与foo()的范围相同,因此可以稍后进行操作。

实现相同功能的更好方法是使用单独的模块:

模块'foo.js':

var tmpObject = new Bar();

module.exports = function foo(){
    // Use tmpObject.
};

第2单元:

var foo = require('./foo');

IEF与命名foo创建函数的性能比较:http://jsperf.com/ief-vs-named-function