JavaScript is a class-free, object-oriented language, and as such, it uses prototypal inheritance instead of classical inheritance. This can be puzzling to programmers trained in conventional object-oriented languages like C++ and Java. JavaScript's prototypal inheritance has more expressive power than classical inheritance, as we will see presently.

But first, why do we care about inheritance at all? There are primarily two reasons. The first is type convenience. We want the language system to automatically cast references of similar classes. Little type-safety is obtained from a type system which requires the routine explicit casting of object references. This is of critical importance in strongly-typed languages, but it is irrelevant in loosely-typed languages like JavaScript, where object references never need casting.

The second reason is code reuse. It is very common to have a quantity of objects all implementing exactly the same methods. Classes make it possible to create them all from a single set of definitions. It is also common to have objects that are similar to some other objects, but differing only in the addition or modification of a small number of methods. Classical inheritance is useful for this but prototypal inheritance is even more useful.

To demonstrate this, we will introduce a little sugar which will let us write in a style that resembles a conventional classical language. We will then show useful patterns which are not available in classical languages. Then finally, we will explain the sugar.

Classical Inheritance

First, we will make a Parenizor class that will have set and get methods for its value, and a toString method that will wrap the value in parens.

function Parenizor(value) {
this.setValue(value);
} Parenizor.method('setValue', function (value) {
this.value = value;
return this;
}); Parenizor.method('getValue', function () {
return this.value;
}); Parenizor.method('toString', function () {
return '(' + this.getValue() + ')';
});

The syntax is a little unusual, but it is easy to recognize the classical pattern in it. The method method takes a method name and a function, adding them to the class as a public method.

So now we can write

myParenizor = new Parenizor(0);
myString = myParenizor.toString();

As you would expect, myString is "(0)".

Now we will make another class which will inherit from Parenizor, which is the same except that its toString method will produce "-0-" if the value is zero or empty.

function ZParenizor(value) {
this.setValue(value);
} ZParenizor.inherits(Parenizor); ZParenizor.method('toString', function () {
if (this.getValue()) {
return this.uber('toString');
}
return "-0-";
});

The inherits method is similar to Java's extends. The uber method is similar to Java's super. It lets a method call a method of the parent class. (The names have been changed to avoid reserved word restrictions.)

So now we can write

myZParenizor = new ZParenizor(0);
myString = myZParenizor.toString();

This time, myString is "-0-".

JavaScript does not have classes, but we can program as though it does.

Multiple Inheritance

By manipulating a function's prototype object, we can implement multiple inheritance, allowing us to make a class built from the methods of multiple classes. Promiscuous multiple inheritance can be difficult to implement and can potentially suffer from method name collisions. We could implement promiscuous multiple inheritance in JavaScript, but for this example we will use a more disciplined form called Swiss Inheritance.

Suppose there is a NumberValue class that has a setValue method that checks that the value is a number in a certain range, throwing an exception if necessary. We only want its setValue and setRange methods for our ZParenizor. We certainly don't want its toString method. So, we write

ZParenizor.swiss(NumberValue, 'setValue', 'setRange');

This adds only the requested methods to our class.

Parasitic Inheritance

There is another way to write ZParenizor. Instead of inheriting from Parenizor, we write a constructor that calls the Parenizor constructor, passing off the result as its own. And instead of adding public methods, the constructor adds privileged methods.

function ZParenizor2(value) {
var that = new Parenizor(value);
that.toString = function () {
if (this.getValue()) {
return this.uber('toString');
}
return "-0-"
};
return that;
}

Classical inheritance is about the is-a relationship, and parasitic inheritance is about the was-a-but-now's-a relationship. The constructor has a larger role in the construction of the object. Notice that the uber née super method is still available to the privileged methods.

Class Augmentation

JavaScript's dynamism allows us to add or replace methods of an existing class. We can call the method method at any time, and all present and future instances of the class will have that method. We can literally extend a class at any time. Inheritance works retroactively. We call this Class Augmentation to avoid confusion with Java's extends, which means something else.

Object Augmentation

In the static object-oriented languages, if you want an object which is slightly different than another object, you need to define a new class. In JavaScript, you can add methods to individual objects without the need for additional classes. This has enormous power because you can write far fewer classes and the classes you do write can be much simpler. Recall that JavaScript objects are like hashtables. You can add new values at any time. If the value is a function, then it becomes a method.

So in the example above, I didn't need a ZParenizor class at all. I could have simply modified my instance.

myParenizor = new Parenizor(0);
myParenizor.toString = function () {
if (this.getValue()) {
return this.uber('toString');
}
return "-0-";
};
myString = myParenizor.toString();

We added a toString method to our myParenizor instance without using any form of inheritance. We can evolve individual instances because the language is class-free.

Sugar

To make the examples above work, I wrote four sugar methods. First, the method method, which adds an instance method to a class.

Function.prototype.method = function (name, func) {
this.prototype[name] = func;
return this;
};

This adds a public method to the Function.prototype, so all functions get it by Class Augmentation. It takes a name and a function, and adds them to a function's prototype object.

It returns this. When I write a method that doesn't need to return a value, I usually have it return this. It allows for a cascade-style of programming.

Next comes the inherits method, which indicates that one class inherits from another. It should be called after both classes are defined, but before the inheriting class's methods are added.

Function.method('inherits', function (parent) {
this.prototype = new parent();
var d = {},
p = this.prototype;
this.prototype.constructor = parent;
this.method('uber', function uber(name) {
if (!(name in d)) {
d[name] = 0;
}
var f, r, t = d[name], v = parent.prototype;
if (t) {
while (t) {
v = v.constructor.prototype;
t -= 1;
}
f = v[name];
} else {
f = p[name];
if (f == this[name]) {
f = v[name];
}
}
d[name] += 1;
r = f.apply(this, Array.prototype.slice.apply(arguments, [1]));
d[name] -= 1;
return r;
});
return this;
});

Classical Inheritance in JavaScript的更多相关文章

  1. JavaScript Patterns 6.2 Expected Outcome When Using Classical Inheritance

    // the parent constructor function Parent(name) { this.name = name || 'Adam'; } // adding functional ...

  2. What is the difference between the ways to implement inheritance in javascript.

    see also : http://www.w3school.com.cn/js/pro_js_inheritance_implementing.asp http://davidshariff.com ...

  3. JavaScript面向对象之我见

    序言 在JavaScript的大世界里讨论面向对象,都要提到两点:1.JavaScript是一门基于原型的面向对象语言 2.模拟类语言的面向对象方式.对于为什么要模拟类语言的面向对象,我个人认为:某些 ...

  4. [原创]JavaScript继承详解

    原文链接:http://www.cnblogs.com/sanshi/archive/2009/07/08/1519036.html 面向对象与基于对象 几乎每个开发人员都有面向对象语言(比如C++. ...

  5. JavaScript继承的实现

    怎样在JavaScript中实现简单的继承?  以下的样例将创建一个雇员类Employee,它从Person继承了原型prototype中的全部属性. function Employee(name, ...

  6. JavaScript中的类继承

    JavaScript是一个无class的面向对象语言,它使用原型继承而非类继承.这会让那些使用传统面向对象语言如C++和Java的程序员们感到困惑.正如我们所看到的,JavaScript的原型继承比类 ...

  7. JavaScript继承详解

    面向对象与基于对象 在传统面向对象的语言中,有两个非常重要的概念 - 类和实例. 类定义了一类事物公共的行为和方法:而实例则是类的一个具体实现. 我们还知道,面向对象编程有三个重要的概念 - 封装.继 ...

  8. JavaScript继承详解(四)

    在本章中,我们将分析Douglas Crockford关于JavaScript继承的一个实现 - Classical Inheritance in JavaScript. Crockford是Java ...

  9. JavaScript继承详解(一)

    面向对象与基于对象 几乎每个开发人员都有面向对象语言(比如C++.C#.Java)的开发经验. 在传统面向对象的语言中,有两个非常重要的概念 - 类和实例. 类定义了一类事物公共的行为和方法:而实例则 ...

随机推荐

  1. ul动态增加li

    --> aaa bbb <%@ page language="java" import="java.util.*" pageEncoding=&qu ...

  2. Centos开启FTP及用户配置

    vsftpd作为FTP服务器,在Linux系统中是非常常用的.下面我们介绍如何在centos系统上安装vsftp. 什么是vsftpd vsftpd是一款在Linux发行版中最受推崇的FTP服务器程序 ...

  3. 【STM32】STM32 GPIO模式理解

    stm32的GPIO的配置模式有好几种,包括: 1. 模拟输入: 2. 浮空输入: 3. 上拉输入: 4. 下拉输入: 5. 开漏输出: 6. 推挽输出: 7. 复用开漏输出: 8. 复用推挽输出 如 ...

  4. mooc

    Coursera 课程来源 2014年前已与斯坦福.普林斯顿等近90所大学和教育机构达成合作关系. 用户类型 主要类别为学生.求职者.公司人.其中,求职者可在Coursera上获得<成就报告&g ...

  5. 第 17 章 责任链模式【Chain of Responsibility Pattern】

    以下内容出自:<<24种设计模式介绍与6大设计原则>> 中国古代对妇女制定了“三从四德”的道德规范,“三从”是指“未嫁从父.既嫁从夫.夫死从子”,也就是说一个女性,在没有结婚的 ...

  6. 【网络流24题】 No.14 孤岛营救问题 (分层图最短路)

    [题意] 1944 年,特种兵麦克接到国防部的命令,要求立即赶赴太平洋上的一个孤岛, 营救被敌军俘虏的大兵瑞恩. 瑞恩被关押在一个迷宫里, 迷宫地形复杂, 但幸好麦克得到了迷宫的地形图. 迷宫的外形是 ...

  7. leetcode面试准备: CountPrimes

    1 题目 Description: Count the number of prime numbers less than a non-negative number, n. 接口:public in ...

  8. GDI+编程说明及小结

    原文地址:http://blog.csdn.net/byxdaz/article/details/5972759 GDI+(Graphics Device Interface Plus图形设备接口加) ...

  9. redhat 6.5 使用其它Linux镜像源的yum源

    最近在虚拟机里装了rhel_6.5_x86_64,发现竟然不自带g++,没办法只好 “yum install gcc-c++”,无奈失败,原因是redhat的yum是收费的... 于是打算怒装其它免费 ...

  10. MySQL优化器join顺序

    前一篇介绍了cost的计算方法,下面测试一下两表关联的查询: 测试用例 CREATE TABLE `xpchild` ( `id` int(11) NOT NULL, `name` varchar(1 ...