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
classes
and noconstructors
in a language. ES6class
is only a syntactical sugar - There are no
methods
ormembers
in 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 anew
keyword 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
function
object itself - the
prototype
object, owned by this function - That happens before any code execution even begins, just after code parsing
- the
function
object can be accessed just using function name without parenthesis, for example:myFunction
- prototype object can be accessed using
prototype
property offunction
object, for example:myFunction.prototype
prototype
object is used by JavaScript, when function is invoked as aconstructor
(withnew
keyword) to initialize newly constructed object__proto__
propertyprototype
object ofconstructor function
is reminiscent of what is usually stored inclass
definition, in classical OOP languages like Java and C++constructor function
and itsprototype
object are always come togehterprototype
object 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 objectfunction
object and itsprototype
object, 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 tonull
usingvar obj = Object.create(null)
- Object, which is referenced by
__proto__
property of a given object, is called itsparent
. Thatparent
object can also have__proto__
property to its ownparent
, thus formingprototype chain
prototype chain
of objects orprototypal inheritance chain
is 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
Object
constructor - Specific JavaScript object, referenced by some access path
- A
constructor
with itsprototype
- The problem here in the fact, that we have no
classes
, and JavaScript developers often call itobject
but with meaningtype
. Better not to use termsclass
,type
,object
,object type
, but use only termsconstructor
orconstructor 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
Function
constructor - 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
Function
andObject
related to each-other Function.prototype
object 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, notprototype
property - The most confusing part is:
- Parent of a
someObject
object, referenced bysomeObject.__proto__
property, is commonly called its prototype
- Parent of a
- A
prototype
object of a given function, especially a constructor function- Accessible with
SomeConstructor.prototype
property - That should be called constructor prototype
- Accessible with
- A built-in
Function.prototype
object - A built-in
Object.prototype
object - A prototype of any other built-in constructor, for example
String.prototype
To summarize:
- Only a function may have
prototype
property - Any function has
prototype
property prototype
property of a function holds reference to an auxiliary object, which is used only, when the function is invoked as a constructor, withnew
keyword, and completely ignored for all other regular functions- Any object has prototype chain
- Prototype chain is built using
__proto__
property, notprototype
property - Functions are also objects, and thus have
__proto__
property, referencing directly toFunction.prototype
built-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:
foo
itself andfoo.prototype
, even iffoo
does not going to be used as a constructor foo
inherits directly fromFunction.prototype
foo.prototype
inherits 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 Object
Important 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" object
readA
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 property
Static 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 property
Prototype 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')); // false
Factory 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.