JavaScript Object-Oriented Programming -- Inheritance

The concept most often discussed in relation to OO programming is inheritance. Many OO languages support two types of inheritance: interface inheritance, where only the method signatures are inherited, and implementation inheritance, where actual methods are inherited. Interface inheritance is not possible in ECMAScript, because functions do not have signatures. Implementation inheritance is the only type of inheritance supported by ECMAScript, and this is done primarily through the use of prototype chaining.

 

Prototype Chaining

ECMA-262 describes prototype chaining as the primary method of inheritance in ECMAScript. The basic idea is to use the concept of prototypes to inherit properties and methods between two reference types. Recall the relationship between constructors, prototypes, and instances: each constructor has a prototype object that points back to the constructor, and instances have an internal pointer to the prototype. What if the prototype were actually an instance of another type? That would mean the prototype itself would have a pointer to a different prototype that, in turn, would have a pointer to another constructor. If that prototype were also an instance of another type, then the pattern would continue, forming a chain between instances and prototypes. This is the basic idea behind prototype chaining.

function SuperType() {
	this.property = true;
}

SuperType.prototype.getSuperValue = function() {
	return this.property;
};

function SubType() {
	this.subproperty = false;
}

//inherit from SuperType
SubType.prototype = new SuperType();

//new method
SubType.prototype.getSubValue = function() {
	return this.subproperty;
};

//override existing method
SubType.prototype.getSuperValue = function() {
	return false;
};

var instance = new SubType();
alert(instance.getSuperValue()); //false

Even though prototype chaining is a powerful tool for inheritance, it is not without its issues. The major issue revolves around prototypes that contain reference values. Recall from earlier that prototype properties containing reference values are shared with all instances; this is why properties are typically defined within the constructor instead of on the prototype. When implementing inheritance using prototypes, the prototype actually becomes an instance of another type, meaning that what once were instance properties are now prototype properties. 

A second issue with prototype chaining is that you cannot pass arguments into the supertype constructor when the subtype instance is being created. In fact, there is no way to pass arguments into the supertype constructor without affecting all of the object instances. Because of this and the aforementioned issue with reference values on the prototype, prototype chaining is rarely used alone.

 

Constructor Stealing

In an attempt to solve the inheritance problem with reference values on prototypes, developers began using a technique called constructor stealing (also sometimes called object masquerading or classical inheritance). The basic idea is quite simple: call the supertype constructor from within the subtype constructor. Keeping in mind that functions are simply objects that execute code in a particular context, the apply() and call() methods can be used to execute a constructor on the newly created object.

function SuperType(name) {
	this.name = name;
}

function SubType() {
	//inherit from SuperType passing in an argument
	SuperType.call(this, "Nicholas");

	// instance property
	this.age = 29;
}

var instance = new SubType();
alert(instance.name); //"Nicholas";
alert(instance.age); //29

The downside to using constructor stealing exclusively is that it introduces the same problems as the constructor pattern for custom types: methods must be defined inside the constructor, so there’s no function reuse. Furthermore, methods defined on the supertype’s prototype are not accessible on the subtype, so all types can use only the constructor pattern. Because of these issues, constructor stealing is rarely used on its own. 

 

Combination Inheritance

Combination inheritance (sometimes also called pseudoclassical inheritance) combines prototype chaining and constructor stealing to get the best of each approach. The basic idea is to use prototype chaining to inherit properties and methods on the prototype and to use constructor stealing to inherit instance properties. This allows function reuse by defining methods on the prototype and allows each instance to have its own properties.

function SuperType(name) {
	this.name = name;
	this.colors = [ "red", "blue", "green" ];
}

SuperType.prototype.sayName = function() {
	alert(this.name);
};

function SubType(name, age) {
	SuperType.call(this, name);

	this.age = age;
}

SubType.prototype = new SuperType();

SubType.prototype.sayAge = function() {
	alert(this.age);
};

var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas";
instance1.sayAge(); //29

var instance2 = new SubType("Greg", 27);
alert(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg";
instance2.sayAge(); //27

In this example, the SuperType constructor defines two properties, name and colors, and the SuperType prototype has a single method called sayName(). The SubType constructor calls the SuperType constructor, passing in the name argument, and defines its own property called age. Additionally, the SubType prototype is assigned to be an instance of SuperType, and then a new method called sayAge() is defined. With this code, it’s then possible to create two separate instances of SubType that have their own properties, including the colors property, but all use the same methods.

Addressing the downsides of both prototype chaining and constructor stealing, combination inheritance is the most frequently used inheritance pattern in JavaScript. It also preserves the behavior of instanceof and isPrototypeOf() for identifying the composition of objects.

 

Here gives a comprehensive example about Combination Inheritance.

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Combination Inheritance Example</title>
<script type="text/javascript">
	function Shape(edge) {
		this.edge = edge;
	}

	Shape.prototype.getArea = function() {
		return -1;
	}

	Shape.prototype.getEdge = function() {
		return this.edge;
	}

	function Triangle(bottom, height) {
		Shape.call(this, 3);
		this.bottom = bottom;
		this.height = height;
	}

	Triangle.prototype = new Shape();

	Triangle.prototype.getArea = function() {
		return 0.5 * this.bottom * this.height;
	}

	//Triangle.prototype.getEdge = function()
	//{
	//	return this.edge;
	//}

	var triangle = new Triangle(10, 4);
	//alert(triangle.getEdge());
	//alert(triangle.getArea());

	function Rectangle(bottom, height) {
		Shape.call(this, 4);
		this.bottom = bottom;
		this.height = height;
	}

	Rectangle.prototype = new Shape();

	Rectangle.prototype.getArea = function() {
		return this.bottom * this.height;
	}

	//Rectangle.prototype.getEdge = function()
	//{
	//	return this.edge;
	//}

	var rectangle = new Rectangle(20, 40);
	alert(rectangle.getEdge());
	alert(rectangle.getArea());
</script>
</head>
<body>

</body>
</html>

 

Summary

There are total 6 approaches to implement inheritance, e.g.

  • Prototype Chaining
  • Constructor Stealing
  • Combination Inheritance(The most common way)
  • Prototypal Inheritance
  • Parasitic Inheritance
  • Parasitic Combination Inheritance

The most common way

function SuperType(name) {
	this.name = name;
	this.colors = ["red", "blue", "green"];
}

SuperType.prototype.sayName = function () {
	console.log(this.name);
};

function SubType(name, age) {
	SuperType.call(this, name);

	this.age = age;
}

SubType.prototype = new SuperType();

SubType.prototype.sayAge = function () {
	console.log(this.age);
};

var instance1 = new SubType("Michael", 29);
instance1.colors.push("black");
console.log(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Michael";
instance1.sayAge(); //29

var instance2 = new SubType("Jordan", 27);
console.log(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Jordan";
instance2.sayAge(); //27

 

Reference

Wrox.Professional.javascript.for.Web.Developers.3rd.Edition.2012

Leanpub.Principles.of.Object-Oriented.Programming.in.JavaScript.Jun.2014

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值