这个JavaScript模式叫什么?为什么使用它?
我正在学习THREE.js并注意到一个模式,其中的function定义如下:
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; };
比较第一个版本和正常的版本,第一个版本似乎有所不同:
- 它分配自执行function的结果。
- 它在这个函数中定义了一个局部variables。
- 它返回包含使用局部variables的逻辑的实际函数。
所以主要区别在于,在第一个变体中,在初始化时,条只被分配一次,而第二个变体在每次被调用时创build这个临时variables。
我最好的猜测是为什么使用它,它限制了实例的数量(只有一个),从而节省了内存pipe理开销。
我的问题:
- 这个假设是否正确?
- 这个模式有没有名字?
- 为什么使用?
你的假设几乎是正确的。 我们先来回顾一下。
- 它分配自我执行function的返回
这被称为立即调用函数expression式或IIFE
- 它在这个函数中定义了一个局部variables
这是在JavaScript中使用私有对象字段的方式,因为它不提供private
关键字或function。
- 它返回包含使用局部variables的逻辑的实际函数。
再一次,重点是这个局部variables是私人的 。
这个模式有没有名字?
AFAIK你可以调用这个模式模块模式 。 引用:
模块模式使用闭包封装“隐私”,状态和组织。 它提供了一种包装公有和私有方法和variables混合的方法,防止部分泄漏到全局范围内,并意外地与另一个开发人员的接口相冲突。 有了这个模式,只有一个公共的API被返回,所有其他的封闭在私有。
比较这两个例子,我最好的猜测为什么第一个是使用的是:
- 它正在实施Singletondevise模式。
- 可以使用第一个示例来控制可以创build特定types的对象的方式。 与此有关的一个近似匹配可以是Effective Java中描述的静态工厂方法 。
- 如果您每次都需要相同的对象状态,则效率很高 。
但是如果你每次只需要香草物体,那么这个模式可能不会增加任何价值。
它限制了对象的初始化成本,并确保所有的函数调用都使用相同的对象。 例如,这允许将状态存储在对象中以供将来的调用使用。
虽然它可能会限制内存的使用,但通常GC会收集未使用的对象,所以这种模式不太可能有帮助。
这种模式是一种特定的封闭forms。
我不确定这个模式是否有一个更正确的名字,但是这看起来像一个模块给我,它使用的原因是封装和维护状态。
闭包(由函数内的函数标识)确保内函数可以访问外函数中的variables。
在你给出的例子中,通过执行外部函数返回(并分配给foo
)内部函数,这意味着tmpObject
继续在闭包内生存,并且多次调用内部函数foo()
将对tmpObject
的同一个实例进行tmpObject
。
你的代码和Three.js代码之间的主要区别是在Three.js代码中,variablestmpObject
只被初始化一次,然后被每次调用返回的函数共享。
这对保持调用之间的状态非常有用,类似于类似C语言中的static
variables。
tmpObject
是一个只对内部函数可见的私有variables。
它改变了内存使用情况,但不是为了节省内存而devise的。
我想通过扩展到揭示模块模式的概念来为这个有趣的线程作出贡献,这确保了所有的方法和variables在被明确暴露之前保持私有。
在后一种情况下,添加方法将被称为Calculator.add();
在提供的示例中,第一个片段将对每个对函数foo()的调用使用同一个tmpObject实例,而在第二个片段中,tmpObject将每次都是新实例。
第一个片段可能被使用的一个原因是variablestmpObject可以在对foo()的调用之间共享,而不会将其值泄漏到声明foo()的作用域中。
第一个片段的非立即执行的function版本实际上看起来像这样:
var tmpObject = new Bar(); function foo(){ // Use tmpObject. }
但请注意,此版本的tmpObject与foo()的作用域相同,因此稍后可以对其进行操作。
实现相同function的更好方法是使用单独的模块:
模块“foo.js”:
var tmpObject = new Bar(); module.exports = function foo(){ // Use tmpObject. };
模块2:
var foo = require('./foo');
比较一个IEF和一个名为foo创build者函数的性能: http : //jsperf.com/ief-vs-named-function