Andrew Kelley - Private Methods in JavaScript (2013 Jul 17)

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.

Here's a jsperf for further evidence.

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

Thanks for reading my blog post.

Home Page | RSS feed | Sponsor the Zig Software Foundation