My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.

Monday, April 07, 2008

Natural JavaScript private methods

I've never seen this technique yet, but basically it allows us to create private methods, without privileged, and in an way that does not allow subclasses to inherit them ... does it sound interesting? ;)

As we can read in my JavaScript Prototypal Inheritance for Classical Emulation documentation, there is a way to easily create private methods without usage of privileged.

The advantage of this way is, as explained in my doc, is that JavaScript interpreter does not have to create many functions for each declared instance.

Here there is a basic example:


// our constructor
function Person(name, age){
this.name = name;
this.age = age;
};

// prototype assignment
Person.prototype = (function(){

// we have a scope for private stuff
// created once and not for every instance
function toString(){
return this.name + " is " + this.age;
};

// create the prototype and return them
return {

// never forget the constructor ...
constructor:Person,

// "magic" toString method
toString:function(){

// call private toString method
return toString.call(this);
}
};
})();

// example
alert(
new Person("Andrea", 29)
); // Andrea is 29

Function toString will be shared by prototype with every created instance for the simple reason that every instance will inherit prototype.toString method and, at the same time, it points to private scope where toString function has been defined.
Is everything ok? Perfect, because we have to comprehend quite perfectly above example to understand what we are going to do right now ( and if you do not understand, read my doc to know more :P )

What we have to do each time is to remember that when we need a private method, with our instance injected scope, we have to write in an unnatural way.

What I mean is that if we usually use the underscore prefix to define our virtually protected methods, why couldn't we use them to define a function for private stuff only?

// our constructor
function Person(name, age){
this.name = name;
this.age = age;
};

// prototype assignment
Person.prototype = (function(){

// private stuff
function toString(){
return this.name + " is " + this.age;
};

// prototype
return {

constructor:Person,

toString:function(){

// call private toString method
// in a more natural way
return this._(toString)();
},

// define private methods dedicated one
_:function(callback){

// instance referer
var self = this;

// callback that will be used
return function(){
return callback.apply(self, arguments);
};
}
};
})();

// example
alert(
new Person("Andrea", 29)
); // Andrea is 29

The difference is basically in this line of code:


// instead of this way
return toString.call(this);

// we have this one
return this._(toString)();



Please do not forget that these methods are private, so there is no way to use them in subclasses, if those are created in an external or different closure, and that is exactly an expected behaviour (these functions are private).
But at the same time, if a subclass call an inherited method that use inside the parent prototype the private underscore, it will work perfectly.


// basic extend function
function extend(B, A){
function I(){};
I.prototype = A.prototype;
B.prototype = new I;
B.prototype.constructor = B;
B.prototype.parent = A;
};

// same stuff ...
function Person(name, age){
this.name = name;
this.age = age;
};
Person.prototype = (function(){
function toString(){
return this.name + " is " + this.age;
};
return {
constructor:Person,
toString:function(){
return this._(toString)();
},
_:function(callback){
var self = this;
return function(){
return callback.apply(self, arguments);
};
}
};
})();

// subclass
function Employee(company, name, age){
this.parent.call(this, name, age);
this.company = company;
};

extend(Employee, Person);

Employee.prototype.getFullDetails = function(){
// toString has been inherited from Person
// and it uses inside the private method
return this.toString() + " and works in " + this.company;
};

var other = new Employee("Mega Ltd", "Daniele", 26);
alert(
other.getFullDetails()
);

Finally, what we can do with this method, is to redefine them to allow us to overwrite private functions and/or use them without problems:

// above stuff + subclass
function Employee(company, name, age){
this.parent.call(this, name, age);
this.company = company;
};

extend(Employee, Person);

// extend prototype and return them
Employee.prototype = (function(proto){

function toString(){
return this.company;
};

proto.toString = function(){
return this.parent.prototype.toString.call(this) + " and works in " + this._(toString)();
};

proto._ = function(callback){
var self = this;
return function(){
return callback.apply(self, arguments);
};
};

return proto;
})(Employee.prototype);

alert(new Employee("Mega Ltd", "Daniele", 26));
// Daniele is 26 and works for Mega Ltd


That's it :)

9 comments:

Brett said...

Hi Andrea,

I have to say discovering your blog posts was a real treat. Really awesome, so thank you. You should be required reading after Flanagan, Crockford, Harmes/Diaz. Speaking of which, when are you going to publish a book with all your tricks??

As far as this post, I wanted to tell you (a little late, sorry) that I borrowed your approach in adding true private instance variables that work across the constructor and prototype (with no need for a privileged function bridge). There's a slight disadvantage of depending on adding one public member (a counter) and requiring a little bit of ugly code in places (but the latter problem is solved by copy-and-paste!). I'd be interested to see your reaction...I think the one public member is a lot better than (maybe multiple) privileged functions or public methods with an underscore (that need to be called with "this.").

Cuby said...

I've been looking for a good example of OOP in JavaScript for a very long time. Thank you very much for sharing your experience :)

Jesse said...

The section of your first example where you set Person.prototype can be shortened to this (which is similar to how Employee.prototype is being set up in your last example):


(function(proto){
function toString(){
return this.name + " is " + this.age;
};

proto.toString = function(){
return toString.call(this);
};
})(Person.prototype);


This way there's no danger in forgetting to set the constructor. :)

RyboTech said...
This comment has been removed by a blog administrator.
RyboTech said...

Great post! Helped out a bunch on a refactoring of a project.

Unknown said...

This is a bright idea. I had a project and find out that I need several instances of a lengthy module. With your method I've changed three lines of code: the first, the return, the last and added var x = new XXX() as needed.

I will never write Javascript old school objects again.

Anonymous said...

So wait. How can you make arguments to the constructor saved as private members that are accessible to the prototype's closure? Looks like the only way you communicate between the constructor and the prototype is 'this' (public).

Trem said...

Nice post! I have written a tool to create true private methods on the prototype chain... https://github.com/TremayneChrist/ProtectJS would love some feedback!

Andrea Giammarchi said...

Trem ... I am sorry but I think you confused private with protected, plus I would never use a code based on eval and object.toString() in order to work.

I suggest you some benchmark too but only after the question: do I really need this?