16

This is a follow up to a question I just posted. I'm wondering how you all handle member variables in javascript clases when using MyClass.prototype to define methods.

If you define all of the methods in the constructor function:

function MyClass(){
 this.myMethod = function(){}
}

You can very nicely declare member variables and access them from inside your methods:

function MyClass(){
 var myVar = "hello";
 this.myMethod = function(){
  alert(myVar);
 }
}

When using the Object.prototype technique, you lose this nicety, and have to do it like this;

function MyClass(){}
MyClass.prototype.myVar = "hello";
MyClass.prototype.myMethod = function(){alert(this.hello)};

I'm not crazy about having to write "this" every time I access a member variable. I want to use the Object.prototype approach for memory and flexibility reasons, but it seems a lot clumsier syntax-wise. Is this how you folks generally work?

thanks,

-Morgan

Kenan Banks
  • 207,056
  • 34
  • 155
  • 173
morgancodes
  • 25,055
  • 38
  • 135
  • 187
  • In regard to private variable members, you may be interested to use the Google Closure compiler. Each class can be defined in a separate file and private variables will then be enforced by the compiler. https://developers.google.com/closure/compiler/docs/js-for-compiler#tag-private – Alexis Wilke Apr 11 '14 at 13:10

4 Answers4

21

You should get over your aversion to using the this pointer to access member variables.

Assign member variables in the constructor, and you can access them with prototype methods:

function Cat(){
    this.legs = 4;
    this.temperament = 'Apathetic';
    this.sound = 'Meow';
}

Cat.prototype.speak = function(){alert(this.sound)}

var cat = new Cat();
cat.speak();

Yes those object attributes are public but, as Guido would say, we're all adults here. Javascript is, after all, a plain-text, loosely-typed, interpreted language. The benefits of "private" variables in this environment are shaky at best.

I say just be explicit and obvious about how your object should be accessed, and violators will stray from that at their own risk.

Kenan Banks
  • 207,056
  • 34
  • 155
  • 173
  • The problem, is of course you cannot have private methods/vars. Could you please post a way that you can allow for that using the prototype method? –  Jan 12 '09 at 17:44
  • 1
    @Nelson - prototype methods will never have access to "private variables" (really, local variables to the constructor). See my answer here: http://stackoverflow.com/questions/436120/javascript-accessing-private-member-variables-from-prototype-defined-functions#436147 – Kenan Banks Jan 12 '09 at 17:46
  • so if you use Nelson's technique (also Crockford's if I'm not mistaken), you can have private members, but have potential memory issues, if you use Tryiptych's, you avoid the memory problems, have some additional niceties (being able to extend/modify the prototype without), but lose your privates? – morgancodes Jan 12 '09 at 17:51
  • There's nothing preventing you from combining the different methods. Use private/privileged methods where you need them, and the prototype everywhere else. – Matthew Crumley Jan 13 '09 at 00:48
  • member variables should NOT be created with prototype, because by definition prototype does not reflect instances. The keyword "this" is extremely valuable because it tells a later reader exactly what the scope of the variable in question is (contrary to whatever Crockford says about "this") – annakata Jan 13 '09 at 11:12
  • Variable members defined in your prototype get copied on `new` which means they are explicitly defined. Having definitions in there doesn't hurt, EXCEPT for arrays and objects because only the references of those get copied, which means modifying them from one instance, changes all instances. – Alexis Wilke Apr 11 '14 at 13:08
16

The visiblity of object attributes varies according to how you declare them

function Cat( name ) {

    //private variable unique to each instance of Cat
    var privateName = 'Cat_'+Math.floor( Math.random() * 100 );

    //public variable unique to each instance of Cat
    this.givenName = name;

    //this method has access to private variables
    this.sayPrivateName = function() {
        alert( privateName );
    }
}

//this variable is shared by all cats
Cat.prototype.generalName = 'tiddles';

//this method is shared by all cats and has no access to private vars
Cat.prototype.sayname = function( type ) {
    alert( this[type+'Name'] || 'private!' );
}

var vic = new Cat('Victor');
var ellers = new Cat('Elmore');

vic.sayname('general');    //tiddles
vic.sayname('given');      //Victor
vic.sayname('private');    //private - no access
vic.sayPrivateName();      //cat will say its name

ellers.sayname('general');    //tiddles
ellers.sayname('given');      //Elmore
ellers.sayname('private');    //private - no access
ellers.sayPrivateName();      //cat will say its name
meouw
  • 41,754
  • 10
  • 52
  • 69
  • Note: if your constructor returns anything (like an object, to expose functions or values) then all the members you declared with `this.` are no longer publicly visible. In other words, in a constructor, stick to either `this.` or return an `object` -- don't typically do both. – Matt Feb 03 '14 at 17:28
3

You should use the prototype to store methods, because when you find yourself with 100 methods, they're not copied around between instances rather they use the same prototype. I use something along these lines:

var myClass = function(){};
myClass.prototype = {
    method1: function(){}
    ,method2: function(){}   
};
Luca Matteis
  • 29,161
  • 19
  • 114
  • 169
  • Thanks sktrdie, that's a major reason why I'm switching to using prototype. I'm wondering what the best way to manage member variables is though. Do I just need to resign myself to typing "this" a whole lot more? – morgancodes Jan 12 '09 at 17:37
  • re: "this" : yes, it's one of those things you just have to get used to. – Jason S Jan 12 '09 at 17:52
2

A (not so) small remark on 'private' variables when assigning methods to the prototype:

It's true that you can't use the constructor to create a closure over it's variables, but you can of course surround the prototypical methods with an anonymous function and get private variables shared between instances of the object:

function Foo() {}

(function() {
    var sharedPrivateVar;
    Foo.prototype.methodWithAccessToSharedPrivateVar = function() {};
})();

With some further twiddling, you can implement your own protection mechanisms, eg variables wich can only be read, not written via:

function Foo() {
    this.registerInstance({ bar : 'baz' });
    this.registerInstance = undefined;
}

(function() {
    var store = {}, guid = 0;

    Foo.prototype.registerInstance = function(protectedProperties) {
        this.__guid = ++guid;
        store[this.__guid] = protectedProperties;
    };

    Foo.prototype.getProtectedProperty = function(name) {
        return store[this.__guid][name];
    };

})();

This approach won't suffer from extensive function object and closure creation, but increases lookup-times by a small amount.

Edit: You should also provide a function

Foo.prototype.unregisterInstance = function() {
    delete store[this.__guid];
};

Otherwise, this is a nice way to introduce a memory leak...

Edit2: You can also get around the need for a registerInstance() function with the following pattern:

Foo = (function() {
    var store = {}, guid = 0;

    function Foo() {
        this.__guid = ++guid;
        store[guid] = { bar : 'baz' };
    }

    Foo.prototype.getBar = function() {
        var privates = store[this.__guid];
        return privates.bar;
    };

    Foo.prototype.destroy = function() {
        delete store[this.__guid];
    };

    return Foo;
})();
Christoph
  • 164,997
  • 36
  • 182
  • 240