views:

120

answers:

2

In OO Javascript constructor pattern: neo-classical vs prototypal, I learned that constructors using prototypal inheritance can be 10x faster (or more) than constructors using the so-called neo-classical pattern with closures as proposed by Crockford in his "Good Parts" book and presentations.

For that reason it seems like preferring prototypal inheritance seems like the right thing, in general.

Question Is there a way to combine prototypal inheritance with the module pattern to allow private variables when necessary?

What I am thinking is:

// makeClass method - By John Resig (MIT Licensed)
function makeClass(){
  return function(args){
    if ( this instanceof arguments.callee ) {
      if ( typeof this.init == "function" )
        this.init.apply( this, args.callee ? args : arguments );
    } else
      return new arguments.callee( arguments );
  };
}


// =======================================================

var User = makeClass();

// convention; define an init method and attach to the prototype
User.prototype.init = function(first, last){
  this.name = first + " " + last;
};


User.prototype.doWork = function (a,b,c) {/* ... */ }; 

User.prototype.method2= (function (a,b,c) {

    // this code is run once per class

    return function(a,b,c) {
        // this code gets run with each call into the method 
        var _v2 = 0;
        function inc() {
            _v2++;
        }

        var dummy = function(a,b,c) {
            /* ... */
            inc();
            WScript.echo("doOtherWork(" + this.name + ") v2= " + _v2);
            return _v2;
        };

        var x = dummy(a,b,c);
        this.method2 = dummy; // replace self
        return x;
    };

})();

That isn't quite right. But it illustrates the point.

Is there a way to do this and is it worth it?

+2  A: 

preferring prototypal inheritance seems like the right thing, in general

Well... it's the more natural, native-feeling thing to do in JavaScript, certainly. But so much JavaScript does is wrong that this isn't necessarily a compliment!

Certainly when performance isn't an issue, objects that get their own bound copies of each method are easier to cope with than objects that share their methods, because you can just pass on a reference to object.method without having to make a closure-delegate or function.bind.

Is there a way to combine prototypal inheritance with the module pattern to allow private variables when necessary?

What do you want from private variables? If it's some Java-style idea of security by encapsulation, I'd give that up and just do it the Python way: put an underline on the start of the member name and anyone who wants to use from the outside will be suitably warned that it's unsupported and may screw up. There is never a security boundary inside JavaScript code executing on the same page that would warrant keeping your privates really private.

If what you want is to avoid having to locate the right copy of this when the method is called, you could manually bind methods methods in the initialiser:

var Thing= makeClass();
Thing.prototype.init= function(a) {
    this._a= a;
    this.showA= this.showA.bind(this);
};
Thing.prototype.showA= function() {
    alert(this._a);
};

thing= new Thing(3);
setTimeout(thing.showA, 1000); // will work as `thing` has its own bound copy of `showA`

(function.bind is future-JavaScript that you can hack into the Function.prototype now until browsers support it.)

This naturally loses some of the lightweight nature of prototype-based objects, but at least you can still have them share members that aren't methods, and methods that are never going to be used as delegates, as long as it's clear and you can always remember which methods are the ones that you can use this way.

If you simply want to be able to type a private variable name without having to put this. all the time then, yeah, you'd have to do it with a closure. Your example world maybe be a little clearer called from the initialiser rather than using the first-time-self-writing:

var User= makeClass();
User.prototype.init= function(first, last){
    this.name= first+' '+last;
    this.method2= this._method2factory();
};
User.prototype._method2factory= function() {
    var _v2= 0;
    function inc() {
        _v2++;
    }

    return function method2(a,b,c) {
        /* ... */
        inc();
        WScript.echo('doOtherWork('+this.name+') v2= '+_v2);
        return _v2;
    };
};

But I'm not really sure this gets you much in comparison to just writing this._v2 and this._inc().

bobince
+1  A: 

I'm not totally sure I understand your question. But going from what I think I do understand...

function Foo () { /*constructor*/
    var counter = 0;  /* private variable */
    this.incrementCounter=function () {  /*privileged function */
        counter++
    }
    this.getCounter=function () {   /*privileged function */
        return counter;
    }

}
 /*public functions. Note: this pattern destroys constructor property. 
 Lesson: Don't depend on the constructor property! */
Foo.prototype = {
   toString: function () {
          var string = "";
          var count = this.getCounter();
          while(count--) {
             string+="*"
          }
          return string;
     }  

}

var bar = new Foo();
bar.incrementCounter();
bar.incrementCounter();
bar.toString();  /* in theory, this returns "**".. haven't tested code */
Breton
toString: function(){ return Array(this.getCounter()+1).join('*'); }
J-P
that's just a bit clever!
Breton