javascript prototypes

Javascript Prototypes — The Backbone of Classes

All Javascript types are based internally on objects called prototypes, including Javascript arrays and strings. Developers may also create their own prototypes for customized objects, as this article will explain.

The Javascript standard describes prototypes as template objects for complex Javascript types. Prototypes describe how Javascript ES6 classes work internally (and have been an important part of Javascript for decades).

This article explains the basics of Javascript prototypes, their importance in functional programming, and how they have remained relevant in the era of Javascript classes.

Javascript Prototype Basics

Complex built-in types (like arrays and strings) and custom Javascript objects use prototypes internally. Javascript prototypes implement a complex type’s actions and identify the information that type may hold.

Javascript Prototypes in Built-in Types

Array and string methods for complex object manipulations are part of their object prototypes. For example, the array method values() and the string method substring() are both prototype methods accessible directly through their object or through its prototype.

Direct method calls act on a named variable, while prototype method calls use the syntax OBJECTNAME.prototype.method(). This example shows the difference between direct method calls and prototype method calls. 

// Array prototype accessed through object
let example1a = [1, 2, 3];
let example1b = example1a.values();
for (const value of example1b) {
    console.log(value);  // 1, 2, 3
}

// String prototype accessed directly
let example2a = "My String";
let example2b = String.prototype.substring(example2a, 0, 2);  // "My"

For built-in Javascript objects like arrays and strings, it’s never necessary to access methods directly through the prototype. Knowing how to access methods through the prototype is useful if you want to add methods to a custom object prototype, as the next section explains.

Do not add methods to built-in types. The maintenance headache that comes from modifying built-in types is rarely worth the ability to write an extended method call.

Javascript Prototypes in Custom Objects

Javascript prototypes give developers the ability to build customized objects. Every customized Javascript object inherits from Javascript’s base Object. The Object type is the base of all Javascript prototypes.

Prototypes are visible in the developer console when you build objects. The actual definitions in the developer console are dynamic and collapsible, so they are more complex than the examples in this article.

Three different functional programming syntaxes create custom objects in Javascript: object literals, constructors, and the method Object.create(). Each syntax creates a slightly different object prototype.

This example shows a Javascript prototype created through object literal syntax. The resulting prototype contains direct access to methods from the Javascript Object type.

// Custom object through object literal
let Example = {
    param1: "a",
    param2: "b",
    show: function() {
        console.log(this.param1 + " " + this.param2);
    }
};

// Custom object through object literal prototype
{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
constructor: ƒ Object()
hasOwnProperty: ƒ hasOwnProperty()
isPrototypeOf: ƒ isPrototypeOf()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toLocaleString: ƒ toLocaleString()
toString: ƒ toString()
valueOf: ƒ valueOf()
__defineGetter__: ƒ __defineGetter__()
__defineSetter__: ƒ __defineSetter__()
__lookupGetter__: ƒ __lookupGetter__()
__lookupSetter__: ƒ __lookupSetter__()
get __proto__: ƒ __proto__()
set __proto__: ƒ __proto__()

This example shows a Javascript prototype created through function constructors. The prototype is small, containing only the constructor and methods or properties the developer explicitly adds.

Prototypes created in this way also use the word __proto__ to refer to the Object type they inherit from. This is a standard syntax in prototype inheritance chains.

// Custom object through a function constructor
function Example(a, b) {
    this.param1 = a;
    this.param2 = b;
}

Example.prototype.show = function() {
    console.log(this.param1 + " " + this.param2);
}

// Custom object through a function constructor prototype
{show: ƒ, constructor: ƒ}
show: ƒ ()
constructor: ƒ Example(a, b)
__proto__: Object

This example shows a Javascript prototype created using the Javascript.create() method. This prototype looks most similar to an object literal.

// Custom object through Object.create()
let Example = Object.create({
    param1: "a",
    param2: "b",
    show: function() {
        console.log(this.param1 + " " + this.param2);
    }
});

// Custom object through Object.create() prototype
{param1: "a", param2: "b", show: ƒ}
param1: "a"
param2: "b"
show: ƒ ()
__proto__: Object

Javascript Prototypes in Inheritance

All Javascript prototypes are part of an inheritance chain, since they all inherit from the base Javascript Object. An inheritance chain connects the prototypes of objects together.

In this example, the base class, Example, is extended by another class, Example2. The prototype for Example2 contains the prototype for Example.

// Class Example
function Example(a, b) {
    this.param1 = a;
    this.param2 = b;
}

Example.prototype.show = function() {
    console.log(this.param1 + " " + this.param2);
}

// Class Example2
function Example2(c, d, e) {
    Example.call(c, d);
    this.param3 = e;
}

Example2.prototype.newFunc = function() {
    console.log("subclass!");
}

// Prototype for class Example2
Example2 {param1: "default1", param2: "default2", param3: "default3"}
    param1: c,
    param2: d,
    param3: e,
    newFunc: newFunc(),
    __proto__: Example
        constructor: Example(a, b),
        show: show(),
        __proto__: Object

An instance of the Example2 class has access to methods from both the Example and the Example2 classes.

let test = new Example2(1, 2, 3);
console.log(test.show());  // "1 2";
test.newFunc();            // subclass!

Javascript Prototypes and Classes

With the inclusion of Javascript classes in ES2015 (ES6), Javascript prototypes are not as explicitly crucial as they once were for building customized objects and inheritance chains. The newer Javascript class syntax handles these things implicitly.

Modern Javascript classes, however, rely on prototypes internally just as older syntaxes relied on them explicitly. A modern Javascript class is a set of instructions to create an object, while its prototype is the object’s actual implementation.

Since Javascript classes have some functionality that traditional prototype syntaxes do not, like private fields and methods, it is often best to use classes rather than explicit prototypes if you require that functionality. Devotees of functional programming have engineered some pseudo-workarounds, but they are usually not as reliable as classes because they cannot implement encapsulation so well.

The ES2015 class syntax for the above examples, which creates the same prototype, is:

class Example {
    param1 = "default1";
    param2 = "default2";

    constructor(a, b) {
        this.param1 = a;
        this.param2 = b;
    }
    show() {
        console.log(this.param1 + " " + this.param2);
    }
}

Conclusion

Javascript prototypes are often hidden in modern Javascript programming because the template of an object doesn’t need to be manipulated directly. They can have powerful applications in inheritance, however, and allow for useful object manipulations in modern Javascript.

Start Learning

To learn more about Javascript, check out other Javascript blog posts and enroll in our Javascript Nanodegree programs, including Intermediate Javascript.