OO JavaScript构造函数模式:新古典vs原型
我观看了道格拉斯·克罗克福德(Douglas Crockford)关于Javascript中好的部分的讲话 ,我的眼睛被打开了。 有一次,他说:“Javascript是唯一的语言,好的程序员相信他们可以有效地使用它,而不需要学习它。” 然后我意识到, 我就是那个人。
在那个演讲中,他发表了一些对我来说非常惊人和有见地的发言。 例如,JavaScript是这个星球上最重要的编程语言。 或者它是这个星球上最stream行的语言。 而且,这是以许多严重的方式被打破的。
他对我所做的最令人吃惊的表述是“新的危险”。 他不再使用它了。 他也不使用this
。
他为Javascript中的构造函数提供了一个有趣的模式,允许私有和公共成员variables,并且不依赖于new
,也不依赖this
。 它看起来像这样:
// neo-classical constructor var container = function(initialParam) { var instance = {}; // empty object // private members var privateField_Value = 0; var privateField_Name = "default"; var privateMethod_M1 = function (a,b,c) { // arbitrary }; // initialParam is optional if (typeof initialParam !== "undefined") { privateField_Name= initialParam; } // public members instance.publicMethod = function(a, b, c) { // because of closures, // can call private methods or // access private fields here. }; instance.setValue = function(v) { privateField_Value = v; }; instance.toString = function(){ return "container(v='" + privateField_Value + "', n='" + privateField_Name + "')"; }; return instance; } // usage var a = container("Wallaby"); WScript.echo(a.toString()); a.setValue(42); WScript.echo(a.toString()); var b = container(); WScript.echo(b.toString());
编辑 :代码更新切换到小写类名称。
这种模式已经从Crockford早期的使用模式演变而来。
问题:你使用这种构造函数模式吗? 你觉得可以理解吗? 你有更好的吗?
这看起来像模块模式的非单例版本,借此可以通过利用JavaScript的“闭包”来模拟私有variables。
我喜欢它( 有点… )。 但是我并没有真正看到用这种方法完成的私有variables的优点,特别是当这意味着任何添加的新方法(初始化之后)都不能访问私有variables。
另外,它没有利用JavaScript的原型模型。 所有的方法和属性都必须在每次调用构造函数的时候初始化 – 如果你有构造函数原型中存储的方法,则不会发生这种情况。 事实是,使用传统的构造函数/原型模式要快得多! 你真的认为私有variables使性能命中值得吗?
这种模式对于模块模式来说是有意义的,因为它只被初始化一次(创build一个伪单例),但是我不确定这是否合理。
你使用这种构造函数模式吗?
不,虽然我使用它的单例变体,模块模式…
你觉得可以理解吗?
是的,它的可读性和清晰度很高,但是我不喜欢把所有内容都包含在构造函数中的想法。
你有更好的吗?
如果你真的需要私有variables,那就坚持下去吧。 否则,只需使用传统的构造函数/原型模式( 除非您分享克罗克福德对new
/ this
组合的恐惧 ):
function Constructor(foo) { this.foo = foo; // ... } Constructor.prototype.method = function() { };
其他类似的问题涉及道格的话题:
- 伪古典与“JavaScript的方式”
- JavaScript的“新”关键字被认为是有害的?
我避免这种模式,因为大多数人觉得难以阅读。 我通常遵循两种方法:
-
如果我只有一个东西,那么我使用匿名对象:
var MyObject = { myMethod: function() { // do something } };
-
对于多个我使用标准的JavaScript原型inheritance的东西
var MyClass = function() { }; MyClass.prototype.myMethod = function() { // do something }; var myObject = new MyClass();
(1)阅读,理解和写作要容易得多。 (2)当有多个对象时效率更高。 Crockford的代码每次都会在构造函数中创build一个新的函数副本。 封闭也有更难debugging的缺点。
尽pipe你失去了真正的私有variables,但是你可以用_
作为惯例来给应该被私有的成员加个前缀。
this
在javascript中是一个公认的难题,但是可以使用.apply
和.apply
来正确设置它。 我也经常使用var self = this;
创build一个闭包variables来作为this
在成员函数中定义的函数内部使用。
MyClass.prototype.myMethod = function() { var self = this; // Either function inner1() { this.member(); } inner1.call(this); // Or function inner2() { self.member(); } inner2(); };
你使用这种构造函数模式吗?
不
你觉得可以理解吗?
是的,它非常直接。
你有更好的吗?
我还没有看过这个话题,但我很快就会谈到。 在那之前,我没有看到使用new
和this
的危险, this
是为什么:
在没有听到他的观点的情况下,我只能假设他build议远离这种事情,因为这种事物的性质,以及它是如何根据特定方法执行的环境(直接在原始对象或作为callback等)。 作为一名教育工作者,他可能正在教导这些关键词的避免,因为大部分没有经验的开发人员在不习惯使用该语言的情况下擅长使用JavaScript。 对于熟悉这门语言的经验丰富的开发人员,我并不认为有必要避免使用这种语言的特性,这种特性赋予了它极大的灵活性(这与避免类似的情况完全不同)。 所有这一切,现在我会看。
无论如何,当不使用某种支持dojo.declare
inheritance的框架(如dojo.declare
),或者在编写一个独立于框架的对象时,我现在采取以下方法。
定义:
var SomeObject = function() { /* Private access */ var privateMember = "I am a private member"; var privateMethod = bindScope(this, function() { console.log(privateMember, this.publicMember); }); /* Public access */ this.publicMember = "I am a public member"; this.publicMethod = function() { console.log(privateMember, this.publicMember); }; this.privateMethodWrapper = function() { privateMethod(); } };
用法 :
var o = new SomeObject(); o.privateMethodWrapper();
bindScope
是类似于Dojo的dojo.hitch
或Prototype的Function.prototype.bind
的dojo.hitch
Function.prototype.bind
。
在他的书中,它被称为functioninheritance (第52页)。 我还没有使用它。 如果你学习道格拉斯的javascript,这是可以理解的。 我不认为有更好的办法。 这个是好的,因为它:
-
使程序员可以创build私人成员
-
保护私人会员,并消除这种反对伪装隐私(私人会员以_开头)
-
使inheritance顺利,没有不必要的代码
但是,它有一些缺点。 你可以阅读更多关于它在这里: http : //www.bolinfest.com/javascript/inheritance.php
你使用这种构造函数模式吗?
当我第一次学习JavaScript的时候,我已经使用过这个模式,并且偶然发现了Douglas Crockford的文献。
你觉得可以理解吗?
只要你明白closures,这个方法是清楚的。
你有更好的吗?
这取决于你想要完成什么。 如果你想写一个尽可能的白痴的库,那么我可以完全理解私有variables的使用。 这可能有助于防止用户无意中干扰对象的完整性。 是的,时间成本可能会稍微大一些(大约3倍),但时间成本是一个实际的争论,直到它实际上影响你的应用程序。 我注意到前一篇文章( testing )中提到的testing没有考虑访问对象函数需要多长时间。
我发现原型/构造函数方法通常会导致更快的构build时间,是的,但不一定节省检索时间。 使用原型链来寻找一个函数,而不是直接附加到你正在使用的对象上的函数。 所以,如果你调用了很多原型函数而不是构build很多对象,那么使用Crockford的模式可能更有意义。
我倾向于不喜欢使用私有variables,但是,如果我不写我的代码为大量的观众。 如果我和一群有能力的编程人员在一起,一般来说,如果我自己编写和评论自己,他们会知道如何使用我的代码。 然后我使用类似Crockford的模式sans私人成员/方法的东西。
如果我们需要创build总是相同types的对象,我宁愿使用Prototypal构造函数模式 ,因为它允许通过原型链 自动委托来共享方法和属性。
我们也可以保留私人物品; 看看这个方法:
var ConstructorName = (function() { //IIFE 'use strict'; function privateMethod (args) {} function ConstructorName(args) { // enforces new (prevent 'this' be the global scope) if (!(this instanceof ConstructorName)) { return new ConstructorName(args); } // constructor body } // shared members (automatic delegation) ConstructorName.prototype.methodName = function(args) { // use private objects }; return ConstructorName; }());
我build议审查这个答案,我们可以find一个有趣的方式来创build一个构造函数: 创build一个Javascript对象的不同方式
这是一个例子,您可以使用原型构造函数的方法: 如何在JavaScript中创build自定义错误?
认为可以帮助讨论在这里报告道格拉斯·克罗克福德在他的“高级JavaScript”演示video中解释了什么是这种模式的主要观点。
重点是避免使用新的操作员。 因为当某个人在调用对象构造函数时忘记使用new运算符时,那么在构造函数中添加的对象成员将全部进入全局名称空间。 请注意,在这种情况下,没有运行时警告或错误通知错误,全局名称空间只是被污染。 这已经被certificate具有严重的安全隐患,并成为很多诊断错误的来源。
这是Powerconstructor模式的主要观点。
私有/特权部分是次要的。 以不同的方式做到这一点是可以的。 那么原型成员是不会被使用的。
HTH卢卡