OO Javascript constructor pattern: neo

I watched a talk by Douglas Crockford on the good parts in Javascript and my eyes were opened. At one point he said, something like, "Javascript is the only language where good programmers believe they can use it effectively, without learning it." Then I realized, I am that guy.

In that talk, he made some statements that for me, were pretty surprising and insightful. For example, JavaScript is the most important programming language on the planet. Or it is the most popular language on the planet. And, that it is broken in many serious ways.

The most surprising statement he made, for me, was "new is dangerous". He doesn't use it any more. He doesn't use this either.

He presented an interesting pattern for a constructor in Javascript, one that allows for private and public member variables, and relies on neither new , nor this . It looks like 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()); 

EDIT : code updated to switch to lowercase class name.

This pattern has evolved from Crockford's earlier usage models.

Question: Do you use this kind of constructor pattern? Do you find it understandable? Do you have a better one?


This looks like the non-singleton version of the module pattern, whereby private variables can be simulated by taking advantage of JavaScript's "closures".

I like it (kinda...). But I don't really see the advantage in private variables done in this way, especially when it means that any new methods added (after initialisation) do not have access to the private variables.

Plus, it doesn't take advantage of JavaScript's prototypal model. All your methods and properties must be initialised EVERY time the constructor is called - this doesn't happen if you have methods stored in the constructor's prototype. The fact is, using the conventional constructor/prototype pattern is much faster! Do you really think private variables make the performance hit worth it?

This kind of model makes sense with the module pattern because it's only being initialised once (to create a pseudo-singleton), but I'm not so sure it makes sense here.

Do you use this kind of constructor pattern?

No, although I do use its singleton variant, the module pattern...

Do you find it understandable?

Yes, it's readable and quite clear, but I don't like the idea of lumping everything inside a constructor like that.

Do you have a better one?

If you really need private variables, then stick with it by all means. Otherwise just use the conventional constructor/prototype pattern (unless you share Crockford's fear of the new / this combo):

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

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

Other similar questions relating to Doug's views on the topic:

  • Pseudo-classical vs. “The JavaScript way”
  • Is JavaScript 's “new” Keyword Considered Harmful?

  • I avoid this pattern as most people find it harder to read. I generally follow two approaches:

  • If I only have one of something, then I use anonymous objects:

    var MyObject = {
        myMethod: function() {
            // do something
        }
    };
    
  • For more than one of something I use standard javascript prototypal inheritance

    var MyClass = function() {
    };
    MyClass.prototype.myMethod = function() {
        // do something
    };
    
    var myObject = new MyClass();
    
  • (1) is much easier to read, understand, and write. (2) is more efficient when there are multiple objects. Crockford's code will create a new copy of the functions inside the constructor every time. Closure also has the downside of being more difficult to debug.

    Although you lose truly private variables, you prefix supposed-to-be-private members with _ as a convention.

    this is an admittedly difficult problem in javascript, but can be worked around using .call and .apply to set it up properly. I also often use var self = this; to create a closure variable to use as this inside functions defined within a member function.

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

    Do you use this kind of constructor pattern?

    Nope

    Do you find it understandable?

    Yes, its very straight forward.

    Do you have a better one?

    I haven't watched the talk yet, but I will be getting to it shortly. Until then, I don't see the danger of using new and this , and here's why:

    Without having heard his points, I can only assume that he suggests staying away from such things because of the nature of this , and how it is prone to change depending on the context in which a particular method is executed (directly upon the original object or as a callback, etc). As an educator, he may be teaching avoidance of these keywords because of the demographic of largely unaware and inexperienced developers who dabble in JavaScript without first grokking the nature of the language. For experienced developers who are intimately acquainted with the language, I'm not convinced that it's necessary to avoid this feature of the language, which grant it an amazing amount of flexibility (which is completely different from avoiding things like with ). All that said, I'll be watching it now.

    At any rate, when not using some sort of framework that supports automagic inheritance (like dojo.declare ), or when writing a framework independent object, I currently take the following approach.

    Definition:

    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();
        }
    };
    

    Usage :

    var o = new SomeObject();
    o.privateMethodWrapper();
    

    Where bindScope is a utility function similar to Dojo's dojo.hitch or Prototype's Function.prototype.bind .

    链接地址: http://www.djcxy.com/p/40844.html

    上一篇: 克罗克福德的原型继承

    下一篇: OO Javascript构造函数模式:neo