JavaScript Inheritance: Pseudoclassical vs. Prototypal

JavaScript is a prototypal object-oriented language, which means objects can inherit directly other objects. This is in contrast with the classical languages such as Java, where a subclass inherits from a superclass, and objects are instances of classes.

However, the expressive power of JavaScript do allow both classical and prototypal inheritance.

Pseudoclassical Inheritance

The pattern of pseudoclassical inheritance uses “constructor function” and the “new” operator to create objects, and uses the “prototype” property to build the inheritance chain. A constructor function is given a “prototype” property; this property is inherited by all instances.

The following code creates a constructor function (pseudoclass) “Point”, and then uses the “new” operator to create an instance. It also adds a method to the “prototype” property of “Point”; this is inherited in the instance.

function Point(x, y) {
	this.x = x || 0;
	this.y = y || 0;
}
Point.prototype.add = function() {
	return this.x + this.y;
};
var p = new Point(3, 4);
console.log(p instanceof Point);	// true
console.log(p.add());			// 7

When we call “new Point(3, 4)”, essentially we are doing the following:

  1. Create a new object and it inherits from Point.prototype.
  2. Call Point() within the context of the new object. This creates and initializes the properties of the new object.

Note that, the constructor function “Point” itself is a function object. It is an instance of “Function”, which is a global object in JavaScript.

console.log(Point instanceof  Function);	//true

By convention, we name a constructor function by capitalizing the first letter. Constructor functions should always be called with the new operator. If we call a constructor function without the new operator, the “this” pointer will point to the global object window, which is not meaningful. Note that JavaScript itself does not have any syntax distinction for constructor functions; it is only the programmer’s intention to let a function be a constructor.

Now we can make another pseudoclass which inherits from “Point”.

function Point3D(x, y, z) {
	Point.call(this, x, y);
	this.z = z || 0;
}
Point3D.prototype = new Point();
Point3D.prototype.add_3d = function() {
	return this.x + this.y + this.z;
};
var q = new Point3D(3, 4, 5);
console.log(q instanceof Point);	// true
console.log(q instanceof Point3D);	// true
console.log(q.add());			// 7
console.log(q.add_3d());		// 12

Inside the constructor “Point3D”, we use the “Point.call” method to apply the constructor “Point”. The “call” method is inherited from “Function.prototype”; it calls a function with a given “this” and arguments. Also, we change the prototype property of Point3D to a new Point object.

When we call “new Point3D(3, 4, 5)” to create a new object, we are doing the following:

  1. Create a new object, and it inherits from Point3D.prototype, which is a Point object.
  2. Call Point3D() within the context of the new object.

Prototypal Inheritance

In prototypal inheritance, we directly create a new object from an existing object, without any notion of classes. Here we use “Object.create” to create a new object; it takes a parameter object which will be the prototype for the new object.

var point = {
	x: 0,
	y: 0,
	add: function() {
		return this.x + this.y;
	}
};
var p = Object.create(point);
p.x = 3;
p.y = 4;
console.log(p.add());			// 7

var point_3d = Object.create(point);
point_3d.z = 0;
point_3d.add_3d = function() {
	return this.x + this.y + this.z;
};
var q = Object.create(point_3d);
q.x = 3;
q.y = 4;
q.z = 5;
console.log(q.add());			// 7
console.log(q.add_3d());		// 12

Note that, “p” and “point_3d” are created in the same way. However, we extend “point_3d” with additional properties and methods.

The method Object.create is essentially defined in the following way:

Object.create = function (o) {
	function F() {}
	F.prototype = o;
	return new F();
};

Reference

Prototype-based programming – Wikipedia

Comments

comments