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

Che*_*eso 43 javascript oop constructor class-design

我看了道格拉斯克罗克福德关于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()); 
Run Code Online (Sandbox Code Playgroud)

编辑:更新代码以切换到小写类名.

这种模式是从Crockford早期的使用模型演变而来.

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

Jam*_*mes 16

这看起来像模块模式的非单例版本,通过利用JavaScript的"闭包"可以模拟私有变量.

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

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

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

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

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

你觉得它可以理解吗?

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

你有更好的吗?

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

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

Constructor.prototype.method = function() { };
Run Code Online (Sandbox Code Playgroud)

关于Doug对该主题的看法的其他类似问题:

  • "另外,它没有利用JavaScript的原型模型." +1到目前为止,这是我采用这种方法的最大问题. (2认同)

Cal*_*leb 10

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

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

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

    var MyClass = function() {
    };
    MyClass.prototype.myMethod = function() {
        // do something
    };
    
    var myObject = new MyClass();
    
    Run Code Online (Sandbox Code Playgroud)

(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();
};
Run Code Online (Sandbox Code Playgroud)

  • 匿名对象?你的意思是对象文字吗? (4认同)

Jus*_*son 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();
    }
};
Run Code Online (Sandbox Code Playgroud)

用法:

var o = new SomeObject();
o.privateMethodWrapper();
Run Code Online (Sandbox Code Playgroud)

bindScope实用函数在哪里类似于Dojo dojo.hitch或Prototype Function.prototype.bind.


Ant*_*min 6

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

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

  • 保护私人成员并消除这种假装 - 隐私(私人成员以_开头)

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

但是,它有一些缺点.您可以在此处阅读更多相关信息:http://www.bolinfest.com/javascript/inheritance.php