background image

extend(Author, Person);
Author.prototype.getBooks = function() {
return this.books;
};

通过手工设置 prototype 和 constructor 属性被替换成为简单在在类声明后(在你为 prototype 添加任何方
法之前)立即调用 extend 函数。唯一的问题是父类的名称(Person)在 Author 声明中被硬编码。这可以被
改成更普遍一点的做法:

function extend(subClass, superClass) {
var F = function() {};
F.prototype = superClass.prototype;
subClass.prototype = new F();
subClass.prototype.constructor = subClass;
subClass.superclass = superClass.prototype;
if(superClass.prototype.constructor == Object.prototype.constructor) {
superClass.prototype.constructor = superClass;
}
}

这个版本要更长一些,但却提供了 superclass 属性,这可以为 Author 和 Person 解耦,函数的头 4 行代码
跟前面一样,最后三行代码确保了 constructor 属性正确的被指定到了 superclass(甚至 superclass 就是
Object 类本身)。这点在你使用新的 superclass 调用父类的构造体时十分有用。

/* Class Author. */

function Author(name, books) {

Author.superclass.constructor.call(this, name);
this.books = books;

}

extend(Author, Person);

Author.prototype.getBooks = function() {

return this.books;

};

增加 superclass 属性后你可以直接调用父类的函数,如果你想覆盖一个父类方法但又必须访问父类的实
现 方 法 时 , 这 是 非 常 有 用 的 。 例 如 , 要 覆 盖 Person 的 getName 函 数 , 你 可 以 首 先 调 用
Author.superclass.getName 来获取原始的名字然后在后面追加内容:

Author.prototype.getName = function() {

var name = Author.superclass.getName.call(this);
return name + ', Author of ' + this.getBooks().join(', ');

};

原型继承
原型继承是又是另外一种讨厌。我们探讨它最好的方法就是忘记你所有关于类和实例的知识,并仅仅考
虑对象。标准的创建对象方法是使用一个类声明定义对象的结构体,然后通过实例化来创建一个新的对
象。通过这种方法创建的对象拥有所有实例属性的拷贝,加一个独立的对所有实例方法的链接。
在原型继承中,不是通过类来指定对象构造体,而仅仅是简单的创建了一个对象。然后这个对象被新的
对象重用,这要感谢原型链的使用。它被称之为 prototype object(原型对象),因为它使得其他对象看
起来很接近(为了避免和其他 prototype 对象混淆,我们用了斜体字)。这正是原型继承的名字由来。
我们现在使用原型继承重新创建 Person 和 Author 类

/* Person Prototype Object. */