| 2K VIEWS

Private Prototype Functions

BY EDWARD SHIRYAEV

The article presents an elegant technique to have prototype-defined functions private, and as additional bonus, to get access from prototype functions, whether public or private, to private members of an instance.

Preamble

It is well known now how to get privacy for variables and functions of javascript objects. This design pattern was suggested by Douglas Crockford:

http://www.crockford.com/javascript/private.html

The main idea is to put all variables' and functions' declarations inside the object's constructor. This automatically makes them invisible from the outside. Public functions also go inside the constructor but are attached as properties of this item. (Crockford calls such functions privileged, I will call them public throughout the article.) A code inside both private and public functions has access to outer-scoped private variables.

Being almost an ideal pattern, it has one issue, namely each object instance creates its own copy of variables and functions. While it is good with variables — instances should have independent data — it is an obvious overhead with functions that need to be shared between instances to save memory, especially if many objects are manipulated together.

This is where prototype functions come to play. By design, functions defined in a prototype of the object constructor, are reused by all instances created with that constructor. Cool. But there are two issues concerning privacy:

  1. Prototype functions are public by design.
  2. Prototype functions have no access to private variables defined in object constructor.

Thus, we either have:

  1. Privacy with not efficient instance functions, or
  2. Efficient prototype functions with no privacy.

Fortunately, there is a prototype-functions-based design pattern that takes the best from both worlds. It still uses instance functions but at a small amount.

Basic PPF Design Pattern

PPF stands for Private Prototype Functions. Basic PPF solves these issues:

  1. Prototype functions get access to private instance data.
  2. Prototype functions can be made private.

For the first, just:

  1. Put all private instance variables you want to be accessible from prototype functions inside a separate data container, and
  2. Pass a reference to the data container to all prototype functions as a parameter.

It's that simple. For example:

// Helper class to store private data.
function Data() {};

// Object constructor
function Point(x, y)
{
  // container for private vars: all private vars go here
  // we want x, y be changeable via methods only
  var data = new Data;
  data.x = x;
  data.y = y;

  ...
}

// Prototype functions now have access to private instance data
Point.prototype.getX = function(data)
{
  return data.x;
}

Point.prototype.getY = function(data)
{
  return data.y;
}

Using a single name for data container in constructor and data parameter in prototype functions gives uniformity in accessing private variables within a constructor and prototype functions:

data.x
data.y

being in contrast to public members that are accessible via this.

Defined this way, prototype functions can be called from within the constructor and each other being always passed the data reference. But they cannot be called from the outside — as methods of an instance — as the outside code does not have data! (Technically it is possible but with no luck.)

Does it resemble you something? Yes! Prototype functions behave like private ones! To make calling them illegal from outside code, we add a simple check inside to see whether the data reference is passed as the first parameter, and if not, we are throwing an error. (We expect a function is called from the outside without the data parameter.)

Now that we have all our prototype functions private, how can we made some of them public? Perhaps you have already guessed: for each prototype function that needs to be public we simply add same name public wrapper as an instance method inside a constructor. The wrapper is identical by signature to its prototype counterpart, except it lacks the first data parameter. The only job for the wrapper is to call its prototype callee passing it data reference. Defined this way, instance wrappers hide their prototype counterparts from being called from outside code.

Below is a complete example to illustrate the above.

// Helper class to store private data.
function Data() {};

function Point(x, y)
{
  // container for private vars: all private vars go here
  // we want x, y be changeable via methods only
  var data = new Data;
  data.x = x;
  data.y = y;

  // public functions-wrappers call their prototype counterparts

  this.getX = function()
  {    
    return Point.prototype.getX.call(this, data);
  }

  this.getY = function()
  {
    return Point.prototype.getY.call(this, data);
  }

  this.move = function(dx, dy)
  {
    return Point.prototype.move.call(this, data, dx ,dy);
  }

  this.howDistantFrom = function(point)
  {
    return Point.prototype.howDistantFrom.call(this, data, point);
  }
}

// private prototype functions have trailing underscore in their names

// Calculates the distance between this and other point.
Point.prototype.distance_ = function(data, point)
{
  if(!(data instanceof Data))
    throw new Error('Trying to call private method');

  var dx = data.x - point.getX(), dy = data.y - point.getY();
  return Math.sqrt(dx * dx + dy * dy);
}

// public prototype functions: called by instance wrappers only,
// so there is no need to check for 'data'

Point.prototype.getX = function(data)
{
  return data.x;
}

Point.prototype.getY = function(data)
{
  return data.y;
}

Point.prototype.move = function(data, dx, dy)
{
  data.x += dx;
  data.y += dy;
}

Point.prototype.howDistantFrom = function(data, point)
{
  var d = this.distance_(data, point);

  return d < 10 ? 'near' : (d < 100 ? 'middistant' : 'far');
}

//---- usage ----

var p1 = new Point(5, 10);
console.log('p1.x: ' + p1.getX());
console.log('p1.y: ' + p1.getY());

var p2 = new Point(10, 15);
console.log('p2.x: ' + p2.getX());
console.log('p2.y: ' + p2.getY());

console.log('p1 is ' + p1.howDistantFrom(p2) + ' from p2');

p2.move(50, 50);
console.log('p1 is ' + p1.howDistantFrom(p2) + ' from p2');

p2.move(50, 50);
console.log('p1 is ' + p1.howDistantFrom(p2) + ' from p2');

p1.distance_(); // issues an error

Improved PPF Design Pattern

Basic PPF presented in previous chapter, while suitable for use as is, can be further improved as to automating the generation of public instance wrappers. Let us look at this code:

function PpfBinder(data)
{
  var proto = Object.getPrototypeOf(this);
  if(!proto)
    return;

  for(key in proto)
    if(typeof proto[key] == 'function' && key.charAt(key.length - 1) != '_')
      this[key] = proto[key].bind(this, data);
}

// Helper class to store private data.
function Data() {};

// Object constructor
function Point(x, y)
{
  // container for private vars: all private vars go here
  // we want x, y be changeable via methods only
  var data = new Data;
  data.x = x;
  data.y = y;

  ...

  PpfBinder.call(this, data);  
}

We introduced base PpfBinder class and make our Point class inherited from it (by calling its constructor inside Point's constructor).

Code inside PpfBinder constructor has the reference to private instance data and this pointing to the object instance. The code gets prototype of the instance and walks through to enumerate all prototype-defined functions.

Among prototype functions, PpfBinder chooses public (those that do not have trailing underscore in their names), and creates for each one a new same name public instance function that uses the body of the original prototype function being callable without data parameter but still passing it internally when called to the original function as the first parameter. If it seems unclear, please consult bind function for details.

Inheritance model with ppfBinder can be replaced with defining and calling a procedure like this:

var oop = oop || {};

oop.bindPublicProtoFunctions = function(obj, data)
{
  var proto = Object.getPrototypeOf(obj);
  if(!proto)
    return;

  for(key in proto)
    if(typeof proto[key] == 'function' && key.charAt(key.length - 1) != '_')
      obj[key] = proto[key].bind(obj, data);
}

// Helper class to store private data.
function Data() {};

// Object constructor
function Point(x, y)
{
  // container for private vars: all private vars go here
  // we want x, y be changeable via methods only
  var data = new Data;
  data.x = x;
  data.y = y;

  ...

  oop.bindPublicProtoFunctions(this, data);
}

Below is complete code for our example using improved PPF design pattern:

var oop = oop || {};

oop.bindPublicProtoFunctions = function(obj, data)
{
  var proto = Object.getPrototypeOf(obj);
  if(!proto)
    return;

  for(key in proto)
    if(typeof proto[key] == 'function' && key.charAt(key.length - 1) != '_')
      obj[key] = proto[key].bind(obj, data);
}

// Helper class to store private data.
function Data() {};

// Object constructor
function Point(x, y)
{
  // container for private vars: all private vars go here
  // we want x, y be changeable via methods only
  var data = new Data;
  data.x = x;
  data.y = y;

  oop.bindPublicProtoFunctions(this, data);
}

// private prototype functions have trailing underscore in their names

// Calculates the distance between this and other point.
Point.prototype.distance_ = function(data, point)
{
  if(!(data instanceof Data))
    throw new Error('Trying to call private method');

  var dx = data.x - point.getX(), dy = data.y - point.getY();
  return Math.sqrt(dx * dx + dy * dy);
}

// public prototype functions: called by instance wrappers only,
// so there is no need to check for 'data'

Point.prototype.getX = function(data)
{
  return data.x;
}

Point.prototype.getY = function(data)
{
  return data.y;
}

Point.prototype.move = function(data, dx, dy)
{
  data.x += dx;
  data.y += dy;
}

Point.prototype.howDistantFrom = function(data, point)
{
  var d = this.distance_(data, point);

  return d < 10 ? 'near' : (d < 100 ? 'middistant' : 'far');
}

//---- usage ----

var p1 = new Point(5, 10);
console.log('p1.x: ' + p1.getX());
console.log('p1.y: ' + p1.getY());

var p2 = new Point(10, 15);
console.log('p2.x: ' + p2.getX());
console.log('p2.y: ' + p2.getY());

console.log('p1 is ' + p1.howDistantFrom(p2) + ' from p2');

p2.move(50, 50);
console.log('p1 is ' + p1.howDistantFrom(p2) + ' from p2');

p2.move(50, 50);
console.log('p1 is ' + p1.howDistantFrom(p2) + ' from p2');

p1.distance_(); // issues an error

PPF and Inheritance

PPF can be easily extended to be used with inheritance. In this chapter we will see how the data container for private instance variables can be shared between superclass and subclass. To see how it works, we will define Point3D class inherited from our Point.

First, let's make a little change to our Point constructor:

// Helper class to store private data.
function Data() {};

// Object constructor
function Point(x, y, data)
{
  // container for private vars: now we assume data
  // may also be passed from the outside
  var data = data || new Data;
  data.x = x;
  data.y = y;

  oop.bindPublicProtoFunctions(this, data);
}

We have added data parameter. Defined this way, Point objects may be created alone as before, or else as part of inherited objects as we will see in a moment.

Second, let's add inherits() helper function to our oop namespace:

oop.inherits = function(subclass, superclass)
{
  subclass.prototype = Object.create(superclass.prototype);
  subclass.prototype.constructor = subclass;
}

The function copies a prototype from a superclass to a subclass, and also restores the subclass' constructor.

Third, we now need to exclude 'constructor' from public prototype functions when creating bound wrappers:

oop.bindPublicProtoFunctions = function(obj, data)
{
  var proto = Object.getPrototypeOf(obj);
  if(!proto)
    return;

  for(key in proto)
    if(typeof proto[key] == 'function')
      if(key != 'constructor' && key.charAt(key.length - 1) != '_')
        obj[key] = proto[key].bind(obj, data);
}

Now it's time to define Point3D class inherited from Point:

function Point3D(x, y, z)
{
  var data = new Data;
  data.z = z;

  Point.call(this, x, y, data);
}

oop.inherits(Point3D, Point);

Point3D.prototype.distance_ = function(data, point)
{
  if(!(data instanceof Data))
    throw new Error('Trying to call private method');

  var dx = data.x - point.getX();
  var dy = data.y - point.getY();
  var dz = data.z - point.getZ();

  return Math.sqrt(dx * dx + dy * dy + dz * dz);
}

Point3D.prototype.getZ = function(data)
{
  return data.z;
}

Point3D.prototype.move = function(data, dx, dy, dz)
{
  data.x += dx;
  data.y += dy;
  data.z += dz;
}

We have defined the data container in the Point3D constructor and passed it to the Point constructor. We have also redefined distance_() and move() functions according to 3D and defined new getZ(). The rest is reused from Point.

Below is the complete code:

//---- oop ----

var oop = oop || {};

oop.bindPublicProtoFunctions = function(obj, data)
{
  var proto = Object.getPrototypeOf(obj);
  if(!proto)
    return;

  for(key in proto)
    if(typeof proto[key] == 'function')
      if(key != 'constructor' && key.charAt(key.length - 1) != '_')
        obj[key] = proto[key].bind(obj, data);
}

oop.inherits = function(subclass, superclass)
{
  subclass.prototype = Object.create(superclass.prototype);
  subclass.prototype.constructor = subclass;
}

//---- Point ----

// Helper class to store private data.
function Data() {};

// Object constructor
function Point(x, y, data)
{
  // container for private vars: now we assume data
  // may also be passed from the outside
  var data = data || new Data;
  data.x = x;
  data.y = y;

  oop.bindPublicProtoFunctions(this, data);
}

// private prototype functions have trailing underscore in their names

// Calculates the distance between this and other point.
Point.prototype.distance_ = function(data, point)
{
  if(!(data instanceof Data))
    throw new Error('Trying to call private method');

  var dx = data.x - point.getX(), dy = data.y - point.getY();
  return Math.sqrt(dx * dx + dy * dy);
}

// public prototype functions: called by instance wrappers only,
// so there is no need to check for 'data'

Point.prototype.getX = function(data)
{
  return data.x;
}

Point.prototype.getY = function(data)
{
  return data.y;
}

Point.prototype.move = function(data, dx, dy)
{
  data.x += dx;
  data.y += dy;
}

Point.prototype.howDistantFrom = function(data, point)
{
  var d = this.distance_(data, point);

  return d < 10 ? 'near' : (d < 100 ? 'middistant' : 'far');
}

//---- Point3D ----

function Point3D(x, y, z)
{
  var data = new Data;
  data.z = z;

  Point.call(this, x, y, data);
}

oop.inherits(Point3D, Point);

Point3D.prototype.distance_ = function(data, point)
{
  if(!(data instanceof Data))
    throw new Error('Trying to call private method');

  var dx = data.x - point.getX();
  var dy = data.y - point.getY();
  var dz = data.z - point.getZ();

  return Math.sqrt(dx * dx + dy * dy + dz * dz);
}

Point3D.prototype.getZ = function(data)
{
  return data.z;
}

Point3D.prototype.move = function(data, dx, dy, dz)
{
  data.x += dx;
  data.y += dy;
  data.z += dz;
}

//---- usage ----

var p1 = new Point3D(5, 10, 20);
console.log('p1.x: ' + p1.getX());
console.log('p1.y: ' + p1.getY());
console.log('p1.z: ' + p1.getZ());

var p2 = new Point3D(10, 15, 22);
console.log('p2.x: ' + p2.getX());
console.log('p2.y: ' + p2.getY());
console.log('p2.z: ' + p2.getZ());

console.log('p1 is ' + p1.howDistantFrom(p2) + ' from p2');

p2.move(50, 50, 50);
console.log('p1 is ' + p1.howDistantFrom(p2) + ' from p2');

p2.move(50, 50, 50);
console.log('p1 is ' + p1.howDistantFrom(p2) + ' from p2');

p1.distance_(); // issues an error

Conclusion

Two variations of PPF design pattern — basic and improved — have been presented. You are free to choose either. Basic requires a bit more coding. Improved, while more concise, uses ECMAScript 5 functions:

Object.getPrototypeOf()
Function.prototype.bind()

that not all browsers may support. Fortunately, there are polyfills for them:

http://ejohn.org/blog/objectgetprototypeof/
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

PPF design pattern may be easily used with inheritance. In the suggested schema, container for private instance data is shared between superclass and subclasses.


Last edited: Nov 17, 2013