In case you want to skim the post, I marked information directly related to your example with italics.
Prototypal inheritance
The prototypal inheritance works as follows:
- Each object
o
has a prototype o.__proto__
.
- When you request a property
p
on object o
, then:
- If the object
o
has property p
, o[p]
is returned.
- Otherwise:
- If
o.__proto__ !== null
, then o.__proto__[p]
is returned.
- Otherwise,
undefined
is returned.
This behaviour is recursive, creating a so-called prototype chain. See the example code below:
var o1 = { a: 1 },
o2 = { b: 2 },
o3 = { c: 3 };
o2.__proto__ = o1;
o3.__proto__ = o2;
o3.a + o3.b + o3.c == 6; // true
This is basically all there is to prototypes. It ought to be noted that __proto__
is a deprecated way of accessing the prototype. ECMAScript provides three other (standard) ways:
- the
prototype
property of functions,
- the
Object.create()
method,
- the
Object.getPrototypeOf()
and Object.setPrototypeOf()
methods.
Let us now look at these.
The prototype
property of functions
The basics
Up to ECMAScript 5, the prototype
property of functions was the only way to work with prototypes. It works as follows:
- Each function
f
has a prototype
property.
- When
new f(arg1, arg2, ..., argN)
is invoked, then:
- An empty new object
o
is created, where o.__proto__ == f.prototype
.
- The function
f
is invoked with arguments arg1, arg2, ..., argN
in the context of o
.
- If the function
f
doesn't return any value, then o
is returned.
This behaviour basically mimics classical inheritance. You fill the f.prototype
object with methods that objects obtained by calling new f
will inherit and f
itself serves as a constructor.
The prototype.constructor
property
As far as constructors are concerned, the f.prototype
object contains by default a f.prototype.constructor
property, which equals to f
. This can be used to find out the object's constructor either by checking out the constructor
property directly or by using the instanceof
syntactic sugar:
var o = new f;
o.constructor == f; // true
o instanceof f; // true
In your first example, you changed the prototype.constructor
of both Square
and Rectangle
to Square
. Therefore:
var o = new Rectange;
o instanceof Rectangle; // false
o instanceof Square; // true
This usually isn't what you want.
Extending the prototypes of built-in objects
Extending the prototype
object of built-in functions / constructors (String
, Array
, Number
, RegExp
, ...) can be a useful application of this principle:
Array.prototype.last = function() {
return this[this.length - 1];
};
[ 1, 2, 3, 4, 5 ].last(); // 5
The Object.create()
method
The Object.create()
method is a part of the ECMAScript 5 and allows you to create new objects with specified prototypes without having to create constructor functions. This is the initial __proto__
example code rewritten using the Object.create()
method:
var o1 = Object.create(null, {
a: {
writable: true,
configurable: true,
value: 1
}
}), o2 = Object.create(o1, {
b: {
writable: true,
configurable: true,
value: 2
}
}), o3 = Object.create(o2, {
c: {
writable: true,
configurable: true,
value: 3
}
});
o3.a + o3.b + o3.c == 6; // true
Setting the object's properties using the second argument of Object.create()
can get quite wordy. If you don't need the flexibility ECMAScript 5 offers (creating immutable properties, properties with setters and getters, ...), you can omit the second argument and fill the object with values as usual:
var o1 = Object.create(null);
o1.a = 1;
var o2 = Object.create(o1);
o2.b = 2;
var o3 = Object.create(o2);
o3.c = 3;
o3.a + o3.b + o3.c == 6; // true
In your second example, you changed the prototype
property of Square
to Object.create(Rectangle.prototype)
and then set Square.prototype
to Rectangle.prototype
. Since the Object.create()
method returns an empty object with, you don't override anything in the Rectangle
's prototype by assigning to it, so:
var o1 = new Rectange;
var o2 = new Square;
o1 instanceof Rectangle; // true
o2 instanceof Square; // true
The Object.getPrototypeOf()
and Object.setPrototypeOf()
methods
The Object.getPrototypeOf()
and Object.setPrototypeOf()
methods are a part of the ECMAScript 6 draft and are just a getter and a setter to the __proto__
attribute. This is the initial __proto__
example code rewritten using the Object.setPrototypeOf()
method:
var o1 = { a: 1 },
o2 = { b: 2 },
o3 = { c: 3 };
Object.setPrototypeOf(o2, o1);
Object.setPrototypeOf(o3, o2);
o3.a + o3.b + o3.c == 6; // true