This post is actual only for:
- ECMA-262 5th edition called ECMAScript 5 (ES5)
- ECMA-262 6th edition called ECMAScript 2015 (ES6)
This is a beginner-level post to clarify and visualize JavaScript prototype-based inheritance.
A lot of incomplete and even wrong info can be found on Internet about JavaScript prototypal inheritance. I will just try to explain it again with help of diagrams.
Understanding JavaScript inheritance mechanics is important, even if you don't plan to use JavaScript OOP patterns, since many of language built-in functionality based on inheritance.
I am not advocating of using OOP patterns and "classical"-like inheritance in JavaScript at all. I personally prefer using "factory" and "mixin" instead of a "constructor" pattern. But this post is not about patterns, it is only about JavaScript prototype-based inheritance mechanics visualization.
Diagrams notation:
- Blocks are JavaScript objects
- Title of the block denotes an accessor to that object
- All other sections in a block are properties of this object
- Arrows are references, with meaning, that given property holds reference to a pointed object. Source of arrow is important, it identifies property, but end is not, it is always points to another object
- Prototype chain, which is used by JavaScript inheritance system is colored in red
- Built-in porperties, are not listed and shortened to
<built-ins>
This post is all about these diagrams:
If you understand them completely - you have done, if not - below is a more detailed explanation.
- In JavaScript there are functions and objects
- There is no
classesand noconstructorsin a language. ES6classis only a syntactical sugar - There are no
methodsormembersin an object, there are onlyproperties. Exception:get()andset()special syntax. - Object property can hold a value or reference to another object or function
- Functions are also objects, but of special type
- Any function can be invoked as a constructor, but this doesn't mean it should be invoked as a constructor
- Functions, which are intended to be used as constructors, just called
constructor functions. Tey have to be invoked with anewkeyword to construct a new object - By convention, constructor functions are named with
PascalCase, all other functions are named withcamelCase - Function in JavaScript is a main force. It can be used as:
- a function
- an instance method
- a static method
- an object, because it is an object
- a constructor
- a name space
- a closure to capture context
- ...
- Every function declaration immediately creates TWO OBJECTS:
- the
functionobject itself - the
prototypeobject, owned by this function - That happens before any code execution even begins, just after code parsing
- the
functionobject can be accessed just using function name without parenthesis, for example:myFunction- prototype object can be accessed using
prototypeproperty offunctionobject, for example:myFunction.prototype prototypeobject is used by JavaScript, when function is invoked as aconstructor(withnewkeyword) to initialize newly constructed object__proto__propertyprototypeobject ofconstructor functionis reminiscent of what is usually stored inclassdefinition, in classical OOP languages like Java and C++constructor functionand itsprototypeobject are always come togehterprototypeobject does not used at all, if function is not intended to be used as a constructor
- Every object has a built-in
__proto__property __proto__property correspond to internal, hidden[[Prototype]]property of the objectfunctionobject and itsprototypeobject, both, also have__proto__property__proto__property as an accessor, standardized only in ES6. In ES5, existance of__proto__property depends on implementation. In ES5 standard way to access value of[[Prototype]]property isObject.getPrototypeOf()method- In ES6
__proto__property can be set, it just holds reference to another object. In ES6 there is also aObject.setPrototypeOf()method - It is possible to create object with
__proto__property set tonullusingvar obj = Object.create(null) - Object, which is referenced by
__proto__property of a given object, is called itsparent. Thatparentobject can also have__proto__property to its ownparent, thus formingprototype chain prototype chainof objects orprototypal inheritance chainis a way, how inheritance is implemented in JavaScript- When JavaScript runtime looks for a property, with a given name, on an object, it first examines object itself, and then all objects down its prototype chain
This is a list of most popular JavaScript built-in constructors. They are constructors, not just functions, objects or namespaces - this is important!
- Array
- Boolean
- Date
- Error, and its derrivatives
- Function
- Math
- Number
- Object
- RegExp
- String
More reading: [MDN Standard built-in objects] (https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects)
Most confusing, of course, are Function and Object. Technically, they both are functions, constructor functions.
Close your eyes and take it as given.
JavaScript authors have named a function, with the name Object(). Then, they have made another function in the system, with the name Function(). And then, they have made technically every function in the system to be an object.
Of course, this is confusing. But, only for the first time. Then, you get used.
Speaker always need to be precisely clear, what he is talking about.
Meanings of term "object":
- Built-in
Objectconstructor - Specific JavaScript object, referenced by some access path
- A
constructorwith itsprototype- The problem here in the fact, that we have no
classes, and JavaScript developers often call itobjectbut with meaningtype. Better not to use termsclass,type,object,object type, but use only termsconstructororconstructor function
- The problem here in the fact, that we have no
- JSON object
- JSON stands for "JavaScript Object Notation"".
- Typical misuse and misunderstanding is, that JSON is not an object, it is always a
string, which will become an object in the memory only after parsing
- POJO, which stands for "Plain Old Javascript Object", or just "simple object"
- This is an object without any custom prototype chain, or any added "methods", just a container for data
- Its
__proto__property refers directly toObject.prototype, or equal tonull - Can be considered as a Hash
Meanings of term "function":
- Built-in
Functionconstructor - Specific named JavaScript function, referenced by name created with
function declaration - Specific anonymous JavaScript function, referenced by some access path
Relation between Function and Object constructors is very important. It plays major role in JavaScript inheritance.
To summarize:
- Every function in JS is an object, more exactly - two objects: function itself and its prototype
- There are two distinct built-in constructor functions
FunctionandObjectrelated to each-other Function.prototypeobject itself inherits fromObject.prototype- Every function in the system inherits from
Function.prototype - Every object in the system inherits from
Object.prototype
The prototypal inheritance chain is drawn in red.
As you may see Function and Object are both functions, thus, they both have prototype property, which holds reference to respective prototype object.
Function and Object are both functions, thus their __proto__ property, refers to Function.prototype, which itself has __proto__ property referencing to Object.prototype.
Both prototype and __proto__ properties of a Function refer to the same Function.prototype object, which is a unique situation, valid only for built-in Function constructor.
When one says word "prototype", it immediately starts a real mess in heads of audience. Speaker always need to be precisely clear, what he is talking about.
Meanings of term "prototype":
- A "prototype" of a given object
- Its parent
- Accessible with
someObject.__proto__property, notprototypeproperty - The most confusing part is:
- Parent of a
someObjectobject, referenced bysomeObject.__proto__property, is commonly called its prototype
- Parent of a
- A
prototypeobject of a given function, especially a constructor function- Accessible with
SomeConstructor.prototypeproperty - That should be called constructor prototype
- Accessible with
- A built-in
Function.prototypeobject - A built-in
Object.prototypeobject - A prototype of any other built-in constructor, for example
String.prototype
To summarize:
- Only a function may have
prototypeproperty - Any function has
prototypeproperty prototypeproperty of a function holds reference to an auxiliary object, which is used only, when the function is invoked as a constructor, withnewkeyword, and completely ignored for all other regular functions- Any object has prototype chain
- Prototype chain is built using
__proto__property, notprototypeproperty - Functions are also objects, and thus have
__proto__property, referencing directly toFunction.prototypebuilt-in object. Their prototype chain is alwayssomeFunction -> Function.prototype -> Object.prototype - All prototype chains ends with
Object.prototype Object.prototype.__proto__holdsnull. This is real end of prototype chain
Having simple function declaration, we get following inheritance.
function foo(){}What we can see:
- function declaration creates two objects:
fooitself andfoo.prototype, even iffoodoes not going to be used as a constructor fooinherits directly fromFunction.prototypefoo.prototypeinherits fromObject.prototype- this inheritance valid for any, even anonymous or arrow function.
What we don't see is that foo itself has internal [[Code]] property, which cannot be accessed but is used when we invoke it with foo().
When you use foo.someMethod(), all built-in methods come from Function.prototype and down the chain from Object.prototype. But foo.someMethod() never comes from foo.prototype. See Static methods.
foo.prototype typically does not used at all, if function is not a constructor, and vice versa, is used in the case of a constructor function.
foo.prototype can be set to any other object reference or primitive value. Setting it to a new object or define new properties on it is a common pattern to define a class.
ES6 arrow function do not have prototype object created by default. It cannot be used as a constructor because it has lexical this and cannot initialize new instance properties.
Simple objects in JavaScript created with object literal syntax or with Object.create function.
// Simple object literal
var foo = {
a: 10
};
// foo object will be used as prototype for bar
var bar = Object.create(foo, {
b: {
value: 20
}
});
console.log(bar.__proto__ === foo); //true
console.log(bar.a); // 10 - Inherited property
console.log(bar.b); //20 - Own porperty
console.log(foo instanceof Object); // true, foo inherits from Object
console.log(bar instanceof Object); // true, bar inherits from ObjectImportant moment here is that in case of changing bar.a value, JavaScript automatically creates bar.a own property with new value, to prevent prototype pollution.
Even if prototype chain of foo and bar looks very simplistic, we can note, that both have an inherited constructor property, which points to the Object constructor, which itself inherits from Function.prototype. More of that, there are a lot of built-in methods in Object.prototype itself, not displayed for clarity. They all are accessible on foo and bar.
Now, let's declare a simple constructor function and create an object instance using it.
function Bar() {
// "this" will point to newly created object
this.a = 10;
}
Bar.prototype.readA = function () {
// "this" will point to the object, in context of which, method is invoked
return this.a;
}
Bar.prototype.writeA = function (a) {
this.a = a;
}
// Constructor function invocation requires "new" keyword
var bar = new Bar();
console.log(bar.constructor === Bar); // true, "constructor" - inherited property
console.log(bar instanceof Bar); // true
console.log(bar instanceof Object); // true, bar inherits from Object
console.log(bar.readA()); // 10 - Invoking inherited method in context of "bar" object
bar.writeA(20);
console.log(bar.readA()); // 20
console.log(bar.a); // 20 - Reading own porperty of "bar" objectreadA and writeA are just regular JS functions with similar references to Function and Object as Bar function itself. These references are not shown for clarity. Important difference between them and Bar function is, that their prototypes are not of any use.
bar object has its own property a, because this property created every time Bar constructor is invoked. This behavior allows to produce different objects with their own property a, but inheriting "methods" from Bar.prototype.
There is no such thing like static method in JavaScript spec at all, but this design pattern can easily be implemented by putting properties on a constructor function object itself, instead of its prototype object.
function Bar() {
this.a = 10;
}
Bar.staticMethod = function () {
// can not use "this" here
return "I am static";
}
Bar.prototype.readA = function () {
return this.a;
}
Bar.prototype.writeA = function (a) {
this.a = a;
}
var bar = new Bar();
console.log(bar.staticMethod); // undefined, method can not be invoked on instance
console.log(Bar.staticMethod()); // "I am static"
console.log(bar.constructor.staticMethod()); // "I am static", method available on instance through inherited constructor propertyStatic methods of constructor are not accessible on instances, created with this consrtuctor, they are available on a constructor itself.
A lot of useful design patterns in JavaScript are implemented putting methods on a constructor function object, for example factory functions. Such a constructor can be used as a namespace or singleton.
// Parent constructor
function Duck (name) {
this.name = name;
};
// Parent method
Duck.prototype.quack = function () {
return this.name + " Duck: Quack-quack!";
};
// Child constructor
function TalkingDuck (name) {
// Call parent constructor with proper arguments
Duck.call(this, name); // This is often forgotten
}
// Inheritance
TalkingDuck.prototype = Object.create(Duck.prototype);
TalkingDuck.prototype.constructor = TalkingDuck; // This is often forgotten
// Method overload
TalkingDuck.prototype.quack = function () {
// Call parent method
return Duck.prototype.quack.call(this) + " My name is " + this.name;
};
// Object instantiation
var donald = new TalkingDuck("Donald");
console.log(donald.quack()); // "Donald Duck: Quack-quack! My name is Donald"
console.log(donald.__proto__ === TalkingDuck.prototype); // true
console.log(TalkingDuck.prototype.__proto__ === Duck.prototype); // true, result of invoking Object.create()
console.log(Duck.prototype.__proto__ === Object.prototype); // true
console.log(donald.quack === TalkingDuck.prototype.quack); // true, method found by prototype chain
console.log(donald instanceof TalkingDuck); // true
console.log(donald instanceof Duck); // true
console.log(donald.name); // "Donald", reading own propertyPrototype chain of donald is donald -> TalkingDuck.prototype -> Duck.prototype -> Object.prototype. Function.prototype does not taking part in this chain, since donald is not a function.
Note that name is own property of donald, though it is created with Duck constructor. This is because Duck constructor is invoked with Duck.call(this, name), where this points to newly created object inside TalkingDuck constructor and then passed down as an invocation context to Duck constructor. See MDN call() reference
Prototype pollution is changing properties of objects taking part in a prototype chain, affecting all other existing instances. Usually these objects are of type SomeConstructor.prototype. Setting properties values on them makes these properties visible for all instances of SomeConstructor and its descendant constructors resulting in unpredictable or undesired behaviours.
Another case is an incorrect constructor function design. The rule of thumb, is not to put value holding properties on a constructor prototype object, but only initialize them inside constructor function body. Constructor prototype should contain only methods.
Wrong!
function Collection(){}
// Shared between instances, - wrong
Collection.prototype.elements = [];
Collection.prototype.add = function (x) {
// Each instance adds values to the same array
this.elements.push(x);
};It should be
function Collection(){
// Each instance gets its own array, - good
this.elements = [];
}
// No changes in code, but result will be different
Collection.prototype.add = function (x) {
this.elements.push(x);
};Next case of prototype pollution is any kind of modification of built-in constructor prototype object, for example Object.prototype or Function.prototype. Sometimes it is really tempting and some frameworks do it as a feature, but now it is considered strongly as an anti-pattern by JS community.
Factory function in JavaScript is not a language feature, it is a pattern. Many patterns in JavaScript are available just because of the power of prototype-based inheritance.
Factory function is very popular and powerful JavaScript OOP pattern. There are hundreds of implementations of it in many libraries and frameworks.
Factory function pattern is simple, it allows you create JavaScript objects and establish prototypal inheritance chain, but without usage of constructor function, using factory function instead. Factory functions can use constructors behind the scenes.
Node.js http.createServer is a typical example of factory function. It returns a new instance of http.Server class. New object is created without using constructor function and new keyword.
// Load the http module
var http = require('http');
// Call factory function to construct a new instance
var server = http.createServer(function (request, response) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.end("Hello World\n");
});
// server is an instance of http.Server class
console.log(server instanceof http.Server); // true
// Call listen method inherited from http.Server class
server.listen(8000);
console.log(server.hasOwnProperty('listen')); // falseFactory function can result in prototype chain similar to what we get using constructors. It is also can be implemented without usage of constructors, replacing them with some sort of init function, but still resulting in a prototype chain of objects. In later case instanceof operator will not work, and developer may need to use Duck typing technique to detect object type.
Mixin is another popular and powerful JavaScript pattern. It is also kind of inheritance but not using prototypal inheritance chain.
Main goals of using mixins are multiple inheritance and performance boost.
Mixin is a function, which borrows behaviours from one or more donor objects to target object. Technically, it consists of copying all methods and properties from donors to target object, allowing it to achieve similar behaviour.
The most important part here, is that methods, copied from donor are not copied or cloned itself. Function objects are not duplicated. Mixin function only copies references to them. All resulting mixed instances refer to the same set of functions.
Multiple inheritance is achieved by mixing behaviours from several objects into one.
Performance boost is achieved by the fact, that prototype chain lookups are expensive, and when all methods from donors are reside directly on a child, it skips lookup and accelerates invocation.
Mixin function holds intact __proto__ property of target object.
instanceof operator will not work.