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

1
  • 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. developers.google.com/closure/compiler/docs/… Commented Apr 11, 2014 at 13:10

4 Answers 4

22

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.

Sign up to request clarification or add additional context in comments.

6 Comments

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?
@Nelson - prototype methods will never have access to "private variables" (really, local variables to the constructor). See my answer here: stackoverflow.com/questions/436120/…
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?
There's nothing preventing you from combining the different methods. Use private/privileged methods where you need them, and the prototype everywhere else.
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")
|
17

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

1 Comment

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

2 Comments

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?
re: "this" : yes, it's one of those things you just have to get used to.
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;
})();

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.