On the GNOME Shell JavaScript Style Guide
GNOME Shell’s Style Guide encourages extension developers to write classes along the lines of:
function ClassName(arg1,arg2) {
this._init(arg1,arg2);
}
ClassName.prototype = {
_init: function(arg1,arg2) {
// initialisation goes here
this._privateMember = arg1;
this.publicMember = arg2;
},
_privateFunction: function() {
},
publicFunction: function() {
}
}
I would contend that this is terrible for a number of reasons.
Le raison numero un: Putting an underscore in front of a method’s name doesn’t make it private. It just puts an underscore in front of the name. Although: it’s well known that JavaScript developers can’t see something when the name is prefixed with underscore. To break this class, it’s a simple matter of writing Classname.prototype._privateFunction = function(){}. Boom.
Le raison numero deux: Separating “function ClassName” and “ClassName._init” is just plain stupid. It add another layer of abstraction for whoever’s reading the code and trying to find out what actually happens when a new ClassName is created.
Le raison numero trois: This kind of “class” has to be instantiated with JavaScript’s “new” keyword. Except… it’s not a class. Nine times, out of ten, it’s not used like a class (at least in the shell, and extensions thereto). It’s an object with an initialiser and some methods and should never be treated otherwise.
So: How else should it be done? How can private methods actually be hidden? How can initialisation logic be integrated? How can “new” be avoided? Like this:
function ClassName(arg1,arg2) {
var privateMember = arg1, self = {};
self.publicMember = arg2;
function privateFunction() {
},
self.publicFunction = function() {
};
return self;
}
With this formulation, private members can’t be touched; the don’t exist outside of the closure. The implementor can control exactly which methods get exported, by adding members to self. And it can be instantiated using var instance = ClassName(“a”,”b”);.
Simples.