OO Javascript构造函数模式:新古典主义与原型

时间:2009-11-27 17:38:44

标签: javascript oop constructor class-design

我看了a talk by Douglas Crockford on the good parts in Javascript和我的眼睛 被打开了。有一次,他说,“Javascript是唯一一种优秀的程序员相信他们可以有效地使用它而不需要学习它的语言。”然后我意识到,我就是那个人。

在那次演讲中,他发表了一些声明,对我来说,非常令人惊讶和富有洞察力。例如,JavaScript是地球上最重要的编程语言。或者它是这个星球上最流行的语言。并且,它以许多严肃的方式被打破。

对我来说,他最令人惊讶的声明是“新的危险”。他不再使用它了。他也没有使用this

他为Javascript中的构造函数提供了一个有趣的模式,一个允许私有和公共成员变量,并且既不依赖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's earlier usage models演变而来。

问题:你使用这种构造函数模式吗?你觉得它可以理解吗?你有更好的吗?

7 个答案:

答案 0 :(得分:16)

这看起来像module pattern的非单例版本,通过利用JavaScript的“闭包”可以模拟私有变量。

我喜欢它(有点...... )。但我并没有真正看到以这种方式完成私有变量的优势,特别是当它意味着添加的任何新方法(在初始化之后)无法访问私有变量。

另外,它没有利用JavaScript的原型模型。每次调用构造函数时,都必须初始化所有方法和属性 - 如果在构造函数的原型中存储了方法,则不会发生这种情况。事实是,使用传统的构造函数/原型模式要快得多!您是否真的认为私有变量会让性能受到重创?

这种模型对模块模式有意义,因为它只被初始化一次(创建一个伪单例),但我不太确定它在这里有意义。

  

你使用这种构造函数模式吗?

不,虽然我确实使用了它的单例变体,模块模式......

  

你觉得它可以理解吗?

是的,它可读且非常清楚,但我不喜欢将所有内容都集中在构造函数中的想法。

  

你有更好的吗?

如果你真的需要私有变量,那么一定要坚持下去。否则只需使用传统的构造函数/原型模式(除非你分享Crockford对new / this组合的恐惧):

function Constructor(foo) {
    this.foo = foo;
    // ...
}

Constructor.prototype.method = function() { };

与Doug关于该主题的观点相关的其他类似问题:

答案 1 :(得分:10)

我避免这种模式,因为大多数人发现它更难阅读。我通常遵循两种方法:

  1. 如果我只有一个东西,那么我使用匿名对象:

    var MyObject = {
        myMethod: function() {
            // do something
        }
    };
    
  2. 对于多个我使用标准javascript原型继承的东西

    var MyClass = function() {
    };
    MyClass.prototype.myMethod = function() {
        // do something
    };
    
    var myObject = new MyClass();
    
  3. (1)更容易阅读,理解和写作。 (2)当有多个对象时效率更高。 Crockford的代码每次都会在构造函数内创建一个新的函数副本。关闭也有更难以调试的缺点。

    虽然您丢失了真正的私有变量,但您可以使用_作为约定将假定成为私有成员作为前缀。

    this在javascript中是一个公认的难题,但可以使用.call.apply来正确设置它。我还经常使用var self = this;创建一个闭包变量,用作成员函数中定义的函数内的this

    MyClass.prototype.myMethod = function() {
        var self = this;
    
        // Either
        function inner1() {
            this.member();
        }
        inner1.call(this);
    
        // Or
        function inner2() {
            self.member();
        }
        inner2();
    };
    

答案 2 :(得分:7)

  

你使用这种构造函数模式吗?

都能跟得上

  

你觉得它可以理解吗?

是的,它很直接。

  

你有更好的吗?

我还没有看过这个演讲,但我很快就会谈到它。 在此之前,我没有发现使用newthis的危险,原因如下:

在没有听到他的观点的情况下,我只能假设他建议远离这些事情,因为this的性质,以及它如何根据执行特定方法的上下文而变化(直接在原始对象上或作为回调等)。作为一名教育工作者,他可能正在教导避免使用这些关键词,因为在没有首先了解语言本质的情况下,他们在很大程度上没有意识到且没有经验的开发人员涉足JavaScript。对于熟悉该语言的有经验的开发人员,我不相信有必要避免使用该语言的这一特性,这使其具有惊人的灵活性(这与避免像with这样的事情完全不同) 。所有这一切,我现在都会看着它。

无论如何,当不使用某种支持自动继承的框架(如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的效用函数。

答案 3 :(得分:6)

在他的书中,它被称为功能继承(第52页)。我还没用。如果你从道格拉斯学习javascript,这是可以理解的。我不认为有更好的方法。这个很好,因为它:

  • 使程序员能够创建私人成员

  • 保护私人会员并取消反对假装隐私(私人会员以_开头)

  • 使继承顺利,没有不必要的代码

然而,它有一些缺点。您可以在此处详细了解:http://www.bolinfest.com/javascript/inheritance.php

答案 4 :(得分:1)

  

你使用这种构造函数模式吗?

我刚开始学习JavaScript时偶然使用过这种模式,偶然发现了Douglas Crockford的文献。

  

你觉得它可以理解吗?

只要您了解闭包,这种方法就很明确了。

  

你有更好的吗?

这取决于你想要完成的任务。如果你正在尝试编写一个尽可能无法证明的库,那么我可以完全理解私有变量的使用。它可能有助于防止用户无意中干扰对象的完整性。是的,时间成本可能略大(大约3倍),但是时间成本是一个没有实际意义的论点,直到它实际影响您的应用程序。我注意到上一篇文章(test)中提到的测试没有考虑访问对象函数所需的时间。

我发现原型/构造函数方法通常会导致更快的构建时间,是的,但它不一定能节省检索时间。使用原型链来查找函数需要额外的成本,而不是将函数直接附加到您正在使用的对象上。因此,如果您调用大量原型函数而不构建大量对象,那么使用Crockford模式可能更有意义。

但是,如果我不是为大量观众编写代码,我倾向于不喜欢使用私有变量。如果我和一群都是有能力的人一起工作,我通常可以相信,如果我自己编码和评论,他们就会知道如何使用我的代码。然后我使用类似于Crockford的模式的东西没有私人成员/方法。

答案 5 :(得分:1)

如果我们需要始终创建相同类型的对象,我宁愿使用 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;
}());

我建议您查看这个答案,我们可以找到一种有趣的方法来创建构造函数: Different way of creating a Javascript Object

这是一个可以使用原型构造函数方法的示例: How do I create a custom Error in JavaScript?

答案 6 :(得分:0)

认为这可以帮助讨论在这里报告道格拉斯·克罗克福德在他的“高级JavaScript”演示视频中所解释的关于这种模式的主要观点。

重点是避免使用new运算符。 因为当有人在调用对象构造函数时忘记使用new运算符时,在构造函数中添加的对象成员都会进入全局命名空间。 请注意,在这种情况下,没有运行时警告或错误通知错误,全局命名空间只是被污染。 事实证明,这具有严重的安全隐患,并且是许多难以诊断的错误的来源。

这是Powerconstructor模式的重点。

私人/特权部分是次要的。可以用不同的方式做到这一点。 原型成员也没有被使用。

HTH luKa