/ javascript

Prototype's real world usage: optimizing memory usage

One of the first things you learn about Javascript when you start delving into the language is that it doesn't support classical inheritance, but rather prototypal inheritance. The topic of prototype-based inheritance is the subject of many online articles and interview questions.
Usually examples used to explain it involve Employees who are also Managers or different talking Animals[1]. So unless you're building an HR app for a zoo, it's hard to come across use-cases for inheritance in your front-end code[2].

talking animals
"Animal Talk Game" by "The Strong" / BY NC ND

This post presents two common scenarios where you should make use of constructor functions and .prototype. In both cases, the reason for doing this is not for modeling inheritance but rather for optimizing memory allocation.
Generally speaking, every time you are creating multiple instances of an object, you could benefit from defining some of its properties using prototype.

Nested Knockout.js ViewModels

If you're using knockout.js it's a likely scenario that you'll need to loop over an array of items and render a piece of UI for each one.
Take a look at this example ViewModel for a form component which is composed of multiple field components.

function FormViewModel(form) {
  var viewModel = this;
  this.fields = [];
  ko.utils.arrayForEach(form.fields, function(field) {
    viewModel.fields.push(new FieldViewModel(field));
  });
}

function FieldViewModel(field) {
  this.key = field.key;
  this.label = field.label;
  this.value = ko.observable(field.value);

  this.save = function() {
    console.log('key "%s" and value "%s"', this.key, this.value());
  };
  this.validate = function() {
    //validate logic
  };
}

Notice the save and validate methods (lines 14-19).
Each of those methods is needlessly defined every time FieldViewModel is instantiated.
Now look at this alternative example.

function FormViewModel(form) {
  var viewModel = this;
  this.fields = [];
  ko.utils.arrayForEach(form.fields, function(field) {
    viewModel.fields.push(new FieldViewModel(field));
  });
}

function FieldViewModel(field) {
  this.key = field.key;
  this.label = field.label;
  this.value = ko.observable(field.value);
}

FieldViewModel.prototype.save = function() {
  console.log('key "%s" and value "%s"', this.key, this.value());
};
FieldViewModel.prototype.validate = function() {
  //validate logic
};

In this example the methods are defined on FieldViewModel.prototype (lines 15-20).
When the methods are defined in this manner, all field instances will share the same function references.
The methods can access the object's properties via the this (line 16).

jQuery plugin authoring

In his free online book "Learning JavaScript Design Patterns" Addy Osmani devotes a chapter to "jQuery Plugin Design Patterns".
It starts off with a fairly simple plugin template. Here it is[3]:

/* jQuery lightweight plugin boilerplate
 * Original author: @ajpiano
 * Further changes, comments: @addyosmani
 * Licensed under the MIT license */
 ;(function ($, window, document, undefined) {
  var pluginName = "defaultPluginName",
    defaults = {
      propertyName: "value"
    };

  function Plugin(element, options) {
    this.element = element;
    this.options = $.extend({}, defaults, options);
    this._defaults = defaults;
    this._name = pluginName;
    this.init();
  }

  Plugin.prototype.init = function () {
  };

  $.fn[pluginName] = function (options) {
    return this.each(function () {
      if (!$.data(this, "plugin_" + pluginName)) {
        $.data(this, "plugin_" + pluginName, new Plugin(this, options));
      }
    });
  }
})(jQuery, window, document);

Notice that when the plugin is called, a new object instance is created using the Plugin constructor function (line 25).
Notice also that the init function is defined on Plugin's prototype (line 19). It's implied that any functions you add to this boilerplate will also be defined on the prototype.

It's likely that our popular plugin will be called on lots of elements on the page (maybe in a loop).
Without prototype each time the plugin is called we'll create a copy of each of the plugin's functions.
With prototype all instances will share the same function definitions.
You can still store instance-specific data by defining it on the this (lines 12-15), and access it from any prototype function.

Analyzing memory usage and execution speed when using prototype

Let's use Chrome's memory-profiling tools to analyze memory usage when using prototype.
I created two jQuery plugins using the template above. They both have 10 identical methods. The only difference between them is how the methods are defined, whether with or without prototype.
Here is a heap snapshot after invoking both plugins on 1,000 elements:

WithoutPrototype uses 13% of the page's memory (465 kb)
WithPrototype uses only 2% of the page's memory (59 kb)

Defining functions using prototype also affects execution speed.
Check out the jsperf "Prototype vs instance functions."
On my machine with Chrome 31, the prototype case was 4 times faster (3,983 ops/sec vs. 1,009 ops/sec).

Footnotes:


  1. In this witty post Alex Sexton illustrates the common trivial approaches to explaining js inheritance to a coworker. ↩︎

  2. If your app really does require using inheritance (or multiple inheritance), I suggest you check out the slidedeck "How we Learned to Stop Worrying and Love JavaScript" by twitter's Angus Croll (which also uses talking animals to get its point across). ↩︎

  3. I removed the comments from the original snippet. You can find the complete version in the online book as "'A Lightweight Start' Pattern." ↩︎