tags:

views:

603

answers:

5

Hello,

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

A: 

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
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
re: "this" : yes, it's one of those things you just have to get used to.
Jason S
A: 

You will find a lot of people using the

function name()
{
    var m_var = null;
    var privateMethod = function() { }
    this.PublicMethod = function() { }
}

Including myself. I tend not to write huge JS classes and this syntax is pretty easy to write and also allows for private methods/variables.

EDIT

So your telling me that using XML for SOAP packets just to make debugging one step easier is alright (even though XML wastes a BUNCH of space and bandwidth, and time parsing), but adding a few bytes per javascript object instance is not alright even if it allows us to use some of the fundemental OO concepts that should of existed in the language from the start?

Hmm...

(for my XML comment read the comments for: http://stackoverflow.com/questions/422188/rest-uris-and-operations-on-an-object-that-can-be-commented-on-tagged-rated-et#422220)

nlaq
-1. He wants to use prototype functions for the memory benefits, which can occasionally be substantial.
Triptych
Thanks Nelson,That's what I've always done, but recently learned that this.PublicMethod will be copied once for every instance. Not a problem if you only have a couple instances, but if you have a lot it can become significant.
morgancodes
One more reason I wanted to use the .prototype method was so that I could optionally override or extend a method. Using your approach, I have to wait until after instantiation to make the change. Nicer if I don't have to worry about that.
morgancodes
+7  A: 

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.

Triptych
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?
nlaq
ok, ok. I guess I will. I'm going to miss my vars though :(
morgancodes
@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
Triptych
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
Thats about it. Also you should look at my answer's edit.
nlaq
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
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
A: 

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
A: 

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