Update 2013-09-14: New sections 1.2, 2 and 3.

This blog post explains when you should and should not put data in prototype properties.

Avoid: prototype properties with initial values for instance properties

Prototypes contain properties that are shared by several objects. As such, they work well for methods. Additionally, with the technique shown below, you can also use them to provide initial values for instance properties. I’ll later explain why that is not recommended.

A constructor usually sets instance properties to initial values. If one such value is a default then you don’t need to create an instance property. You only need a prototype property with the same name whose value is the default. For example:

/** * Anti-pattern: don’t do this * * @param data an array with names */ function Names(data) { if (data) { // There is a parameter // => create instance property this.data = data; } } Names.prototype.data = [];

data

data

Names.prototype.data

The parameteris optional. If it is missing, the instance does not get a property, but inherits, instead.

This approach mostly works: You can create an instance n of Names . Getting n.data reads Names.prototype.data . Setting n.data creates a new own property in n and preserves the shared default value in the prototype. We only have a problem if we change the default value (instead of replacing it with a new value):

> var n1 = new Names(); > var n2 = new Names(); > n1.data.push('jane'); // change default value > n1.data [ 'jane' ] > n2.data [ 'jane' ]

push()

Names.prototype.data

data

n2.data

Best practice: don’t share default values

function Names(data) { this.data = data || []; }

Explanation:changed the array in. Since that array is shared by all instances without an own property’s initial value has changed, too.Therefore, it is better to not share default values and to always create new ones:Obviously, the problem of modifying a shared default value does not arise if that value is immutable (as all primitives [1] are). But for consistency’s sake, it’s best to stick to a single way of setting up properties. I also prefer to maintain the usual separation of concerns [2] : the constructor sets up the instance properties, the prototype contains the methods.

ECMAScript 6 will make this even more of a best practice, because constructor parameters can have default values and you can define prototype methods in class bodies, but not prototype properties with data.

Creating instance properties on demand

function Names(data) { if (data) this.data = data; } Names.prototype = { constructor: Names, get data() { // Define, don’t assign [3] // => ensures an own property is created Object.defineProperty(this, 'data', { value: [], enumerable: true // Default – configurable: false, writable: false // Set to true if property’s value must be changeable }); return this.data; } };

Names.prototype

constructor

Occasionally, creating a property value is an expensive operation (computationally or storage-wise). Then you can create an instance poperty on demand:(As an aside, we have replaced the original object in, which is why we need to set up the property [4] .)

Obviously, that is quite a bit of work, so you have to be sure it is worth it.

Avoid non-polymorphic prototype properties

If the same property (same name, same semantics) exists in several prototypes, it is called. Then the result of reading the property via an instance is dynamically determined via that instance’s prototype. Prototype properties that are not used polymorphically can be replaced by variables (which better reflects their non-polymorphic use).

Example: You can store a constant in a prototype property and access it via this .

function Foo() {} Foo.prototype.FACTOR = 42; // primitive value, immutable Foo.prototype.compute = function (x) { return x * this.FACTOR; };

// This code should be inside an IIFE [5] or a module function Foo() {} var FACTOR = 42; // primitive value, immutable Foo.prototype.compute = function (x) { return x * FACTOR; };

This constant is not polymorphic. Therefore, you can just as well access it via a variable:The same holds for storing mutable data in non-polymorphic prototype properties.

Mutable prototype properties are difficult to manage. If they are non-polymorphic then you can at least replace them with variables.

Polymorphic prototype properties

function ConstrA() { } ConstrA.prototype.TYPE_NAME = 'ConstrA'; function ConstrB() { } ConstrB.prototype.TYPE_NAME = 'ConstrB';

TYPE_NAME

ConstrA

ConstrB

instanceof

References

An example of polymorphic prototype properties with immutable data: Tagging instances of a constructor via prototype properties enables you to tell them apart from instances of a different constructor.Thanks to the polymorphic “tag”, you can distinguish the instances ofandeven when they cross frames (thendoes not work [6] ).