Private Methods in JavaScript
In JavaScript, we don't have private methods right?
We must to resort to using this._somePrivateThing()
right?
Wrong.
Before
function Cell(x, y) {
this.x = x;
this.y = y;
this.things = [1, 2, 3];
// I guess I'll have to use an underscore to indicate that this
// method is private.
this._initializeSomethingElse();
}
Cell.prototype._initializeSomethingElse = function() {
this.dir = Math.atan2(this.y, this.x);
this.total = 0;
// hmm, I need a reference to this in that callback. I'll have to
// save a copy or use bind
var self = this;
self.things.forEach(function(thing) {
self.total += thing;
});
};
After
function Cell(x, y) {
this.x = x;
this.y = y;
this.things = [1, 2, 3];
// boom. private method.
initializeSomethingElse(this);
}
function initializeSomethingElse(self) {
self.dir = Math.atan2(self.y, self.x);
self.total = 0;
self.things.forEach(function(thing) {
self.total += thing;
});
}
Notice that as an added benefit, the new private method is given an explicit
reference to the instance, so if you need to use a callback you don't
have to shuffle around the this
pointer.
"But it will fool the optimizer!"
Wrong again.
Let's benchmark the above examples:
var PrivCell = require('./priv');
var NoPrivCell = require('./no-priv');
console.log("Test 1 - no priv method:", Math.round(test(NoPrivCell)) + "ms");
console.log("Test 1 - private method:", Math.round(test(PrivCell)) + "ms");
console.log("Test 2 - no priv method:", Math.round(test(NoPrivCell)) + "ms");
console.log("Test 2 - private method:", Math.round(test(PrivCell)) + "ms");
console.log("Test 3 - no priv method:", Math.round(test(NoPrivCell)) + "ms");
console.log("Test 3 - private method:", Math.round(test(PrivCell)) + "ms");
function test(Cell) {
var start = new Date();
var total = 0;
for (var i = 0; i < 20000000; i += 1) {
var c = new Cell();
total += c.total;
}
return new Date() - start;
}
Running the benchmark on my machine:
Test 1 - no priv method: 5525ms Test 1 - private method: 5537ms Test 2 - no priv method: 5537ms Test 2 - private method: 5571ms Test 3 - no priv method: 5572ms Test 3 - private method: 5595ms
Makes no difference. It's clean, it solves the problem, and it has no performance implications.
Browser Scoping
Note that if you're writing the code in an environment which does not provide scoping, such as a <script> tag in the browser, you'll want to wrap the entire thing in an anonymous function call:
var Cell = (function() {
function Cell(x, y) {
this.x = x;
this.y = y;
this.things = [1, 2, 3];
// boom. private method.
initializeSomethingElse(this);
}
function initializeSomethingElse(self) {
self.dir = Math.atan2(self.y, self.x);
self.total = 0;
self.things.forEach(function(thing) {
self.total += thing;
});
}
return Cell;
})();