答案已在UPDATE 2/ANSWER中嵌入。
感谢Joseph帮助我找到答案(尽管我不喜欢它=)。
原始问题
在研究JavaScript中使用命名空间的最佳实践时,我发现了“模块模式”的定义:http://yuiblog.com/blog/2007/06/12/module-pattern/。
自从几年前我在YUI2中看到这种模式以来,我一直在普遍使用它,而这篇文章对该概念进行了很好的概述。但是它没有涉及为什么要使用“自执行匿名函数”代替“new function”。虽然有人在评论中提出了这个问题,但作者并没有做出清晰的解释。由于这篇文章已经存在4年之久(我也没有在其他地方找到答案),我想把这个问题带到这里。
它已经在闭包内部,所以呢?(见:为什么这个函数被括号包裹,后面跟着括号? 这篇文章也没有回答我的问题 =)。
假设以下是设置的代码...
var MyNamespace = window.MyNamespace || {};
哪种方式更好并为什么?
MyNamespace.UsingNew = new function() {
var fnPrivate = function() {
return "secrets1";
};
this.property = "value1";
this.method = function() {
return "property = " + this.property + ' ' + fnPrivate();
}
};
MyNamespace.UsingSelfEx = (function() { //# <- Added "pre-parens" suggested by chuckj
var fnPrivate = function() {
return "secrets2";
};
var fnReturn = {};
fnReturn.property = "value2";
fnReturn.method = function() {
return "property = " + this.property + ' ' + fnPrivate();
}
return fnReturn;
})();
更新:
看起来像 jQuery 一样,所有时髦的人都在使用“自执行匿名函数”(SEAF)!但我不理解这个,因为我觉得使用 .UsingNew 方法更加简洁,因为你可以在定义时公开函数(而不是在返回语句中下方需要单独维护或被迫使用内联对象表示法)。
对于不需要“that = this”的论点,我并不认同其中的一些原因:
- 为了避免“var that = this”,你要么最终得到一个“var obj”和一个返回语句(需要维护一个单独的公共定义),要么被迫使用内联对象表示法(返回 {1,2,3})来定义公共属性/方法。
- 您始终可以在类/命名空间顶部创建一个“var that = this”的私有变量,并始终使用“that”。
现在...我想我的开发风格可能更容易管理 .UsingNew 模式。我的“私有”函数几乎总是具有“静态”性质,因此我需要传递上下文(替代“this”)。我还养成了使用“缩写”命名空间的习惯,因此当我需要访问“this”时,我只需通过“缩写”命名空间引用完整对象,而不是通过其完整路径引用。例如:
var PrivateFunct = function() {
var rThis = Cn._.val; //# The abbreviated form of Cn.Renderer.Form.Validation
//...
};
如果它是一个私有静态函数...
var PrivateStaticFunct = function(oContext) {
//...
};
除了上面提到的原因外,我个人发现在源代码中使用.UsingNew方法更易读。我的代码库很大,使用.UsingNew模式:http://code.google.com/p/cn-namespace/source/browse/Cn.Web/js/Cn/,其中验证功能可能是比较容易理解的,只需要快速阅读一遍即可。我也使用一些SEAF函数(请参见ErrorMessages.js.aspx),但仅在它们有意义的情况下使用。
我无法想象必须维护单独的返回值来公开接口!真恶心!
现在,不要误会,有很多地方使用SEAF非常有用,以强制执行闭包,但我个人认为在对象内部过度使用它。
更新2:
经过进一步思考(并感谢与Joseph在他的答案下的详细讨论),似乎可以应用一些规则来适用于此DEATHMATCH:
根据Douglas Crockford(参见:JS we hardly new Ya),匿名函数永远不应使用“new”关键字,因为:
- 使用对象文字更快。
- 通过使用new来调用函数,对象将保留一个无用的原型对象。这浪费了内存而没有抵消优势。如果我们不使用new,我们就不会在链中保留浪费的原型对象。(注意:原型调用在构造函数定义之后进行,由于SEAF或“new”匿名函数即将被触发,因此不能与其一起使用prototype)
- 使用对象文字需要的代码更少。(虽然是真的,但我不同意,因为我讨厌对象文字表示法,我更愿意使用;而不是,,因为它更易读)。
- 直接在函数前面放置new绝不是一个好主意。例如,新函数在构造新对象方面没有优势。
因此,问题的核心在于:由于速度和较少的开销(没有不必要的原型对象),SEAF比var obj = new function() {...};
更可取。您必须遭受的是,您被迫使用对象文字表示法(因此,在公共成员之间使用','而不是';'),或者在返回对象中维护单独的公共对象列表。
当您打算将函数用作对象构造函数时,不建议使用SEAF(Self-Executing Anonymous Function),因为 instanceof
将无法按预期工作(请参见:从JS闭包创建对象:我应该使用“new”关键字吗?)。
- 如果它是一个意图作为Singleton /全局静态实例的匿名函数,请使用SEAF。
- 如果您打算将其用作
构造函数
(可能表示多个对象)或者您正在使用.prototype,则使用“标准”函数定义并使用“new”调用,例如:
function PseudoClass1() {}
var PseudoClass2 = function() {};
var myClass1 = new PseudoClass1();
var myClass2 = new PseudoClass2();
我不得不说,我对这个答案并不满意;) 我发现在代码库中,.UsingNew模式更可读,但是由于未使用的原型引用被实例化并留在对象链中,因此它比SEAF慢且使用更多内存。
new function
。 - Bergi