Skip to content

Instantly share code, notes, and snippets.

@JordanRickman
Last active March 3, 2017 04:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JordanRickman/3be3c262cf8f85d43951eb23b51b1c31 to your computer and use it in GitHub Desktop.
Save JordanRickman/3be3c262cf8f85d43951eb23b51b1c31 to your computer and use it in GitHub Desktop.
Bound Privileged Method Pattern

JavaScript Pattern: The Bound Privileged Method

Information Hiding in JavaScript

In JavaScript, we can exploit closure to create private variables. Like so:

function Constructor() {
  var count = 0;
  this.getCount = function() { return count; };
  this.increment = function() { count++; };
}
var instance = new Constructor();
instance.count; // -> undefined
instance.count = 2; // We can't modify the internal counter
instance.getCount(); // -> 0
instance.increment(); // But mutator methods can modify it
instance.getCount(); // -> 1

However, there is a performance cost to this pattern. Because the methods are defined in the constructor, rather than attached to the prototype, new function objects are created for each instance.

Introducing the Bound Privileged Method

The "bound privileged method" pattern somewhat improves performance over the standard privileged method pattern. By "privileged method" I mean that the method has access to private variables (I am borrowing the term from Douglas Crockford). The method is "bound" because we use the bind method of functions to attach an externally defined function to some private, internal state. Let's jump into the code.

function getCount(internals) { return internals.count; }
function increment(internals) { internals.count++; }
function Constructor() {
  var internals = {
    count: 0
  }
  this.getCount = getCount.bind(this, internals);
  this.increment = increment.bind(this, internals);
}
var instance = new Constructor();
instance.getCount(); // -> 0
instance.increment();
instance.getCount(); // -> 1

Our count variable is still nicely hidden from outside, but now our methods are defined outside our class, with the private variables they need being passed to them. We pass all the private variables as an object so that the privileged methods can update or replace their values - the internals object is shared between all bound privileged methods. Note also that we bind the context to the constructor's this, so the methods can access public properties and methods as well.

I defined getCount and increment in the global scope. When using a module system like npm, that's fine - just don't export them. But in the browser, we can exploit closure to hide our method definitions while still avoiding re-defining them every time the constructor is called.

var Constructor = (function() {
  // Define once, capture via closure
  var getCount = function(internals) { return internals.count; };
  var increment = function(internals) { internals.count++; };
  
  // The actual constructor body
  return function() {
    var internals = {
      count: 0
    }
    this.getCount = getCount.bind(this, internals);
    this.increment = increment.bind(this, internals);
  };
})();
var instance = new Constructor();
instance.getCount(); // -> 0
instance.increment();
instance.getCount(); // -> 1

Performance Metrics

The bound privileged method pattern is not quite as fast as a prototype method - because it still creates new function objects with each call to method.bind(this, internals). But if your method is large and complex, the wrapper function created by bind will be smaller - saving not only memory but allocation and garbage collection time. The code snippet below does some performance testing. In my browser, the bound method test usually ran in 80-90% the time of the standard "constructor method" version, and the prototype method version wasn't much faster - also around 80%, and on some runs slower than the bound method.

function boundMethod(internals) {
  // Let's make this function object take up some memory
  var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;
  // Access a private field
  return internals.field;
}
function Constructor() {
  var internals = {
    field: "private field"
  };
  this.field = "public property";
  this.boundMethod = boundMethod.bind(this, internals);
  this.constructorMethod = function() {
    // Let's make this function object take up some memory
    var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;
    // Access a private field
    return internals.field;
  };
}
Constructor.prototype.prototypeMethod = function() {
  // Let's make this function object take up some memory
  var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;
  return this.field;
}

// noprotect - without "noprotect" in a comment, JSBin thinks our long-running loops are infinite loops and quits ~100ms
// Note that JSBin does not fully support console.time - but if you open the Chrome dev console you'll see the timer output there
var instance;
console.time("constructorMethod");
for (var i = 0; i < 1000000; i++) {
  instance = new Constructor();
  instance.constructorMethod();
}
console.timeEnd("constructorMethod");

console.time("boundMethod");
for (var i = 0; i < 1000000; i++) {
  instance = new Constructor();
  instance.boundMethod();
}
console.timeEnd("boundMethod");

console.time("prototypeMethod");
for (var i = 0; i < 1000000; i++) {
  instance = new Constructor();
  instance.prototypeMethod();
}
console.timeEnd("prototypeMethod");

Summary

The Bound Privileged Method pattern provides a good middle ground between the performance of prototype methods and the information hiding provided by defining a method in the constructor. I hope you find it useful!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment