Brett's Blog
Edit     New

Monday, February 23, 2009

Cleaner private instance methods in JavaScript

Though the Relator requires extra code in places, the benefits are made up in readability in being clear what is private or not, as well as avoid needing the cumbersome 'this' everywhere (whose ubiquity makes its role less clear and also often requires adding a line to assign 'this' to 'that' anyways)

Here I point out a way to avoid needing to make a call() on each reference of a private instance method, again drawing from Andrea Giammarchi's approach to private methods, as I have also covered.

By assigning our static method to the scope of 'this' (as we do if we call call() on the method) and assigning the returned function to a private variable, we can get instance methods throughout our class. The disadvantage is that we add one function per instance (as with privileged methods), but the advantages are 1) the method is truly private, and 2) The syntax is cleaner. If you don't care about #2, just call a regular static method (but which can use 'this') with _privInstanceMethod.call(this, arg1, arg2);




var Constructor = (function () {var __ = Relator.$();

// Harder setup, more memory, easier calling within the class
function _setupPrivInstanceMethod (that) {var _ = __.method(that);
return function () {
alert(_.privateInstanceVariable);
};
}
// Easier setup, and less memory, more complex calling within the class
function _privInstanceMethod () {var _ = __.method(this);
alert(_.privateInstanceVariable);
}

function Constructor () {var _ = __.constructor(this);
_.privateInstanceVariable = 5; // Set up an instance variable to prove we're dealing with an instance method below
_.privInstanceMethod = _setupPrivInstanceMethod(this); // add our instance's scope to return a function which is aware of the instance
}
Constructor.prototype = {
constructor: Constructor,
someMethod : function () {var _ = __.method(this);
_.privInstanceMethod(); // '5'
// more complex call (additional args would get added after 'this')
_privInstanceMethod.call(this); // '5'
}
};
return Constructor;
})();
var a = new Constructor();
a.someMethod();

Relator Zombies

I referred to the "Borg" option in the code comments for this post, in that we can call getAll() on the Relator for any object to access and manipulate any or all private instances variables for other objects already created. Thus, one can create an object, create another object, and then use the latter object to change data on all previous objects.

If we add a 'that' property to all of our Relator instances (easily achievable by adding one line to the middle of the "constructor" method we added for Relator in our last post):


constructor : function (that) {
this.set(that);
this.get(that).that = that;
return this.get(that); // Assign to var _ = __.constructor(this);
},


we can then reference any public methods or members on all objects of this class (along with their private variables) after calling getAll() on the Relator instance:


var Person = (function () {var __ = Relator.$();
function Person (name) {var _ = __.constructor(this);
_.name = name;
}
Person.prototype.getName = function () {var _ = __.method(this);
alert('My name is '+_.name);
};
Person.prototype.zombie = function () {
var zombies = __.getAll();
zombies.forEach(function(zombie){
zombie.that.getName();
// convenient syntax as we could also do something with zombie.name (or access other private instance variables we added)
});
};
return Person;
})();

var bob = new Person('Bob');
var sarah = new Person('Sarah');
sarah.zombie(); // Alerts "My name is Bob" and then "My name is Sarah"



We could also have done the above by adding the following to the Relator class:


getInstances : function () {
return Stack;
},


and then use the following method on Person (instead of the previous implementation):


Person.prototype.zombie = function () {
var zombies = __.getInstances();
zombies.forEach(function(zombie){
zombie.getName();
// var z = __.get(zombie); // Getting a private variable now requires an extra step
// alert(z.name);
});
};


This syntax is shorter to access the whole object and its public methods/members, but does not allow us to use the same syntax to access the zombies' private instances (unless we assign "__.get(zombie);" to a variable within the forEach and access the private data from there, as in the commented out text above).


Google
 
Brett's Blog Web