继承(Inheritance)
Javascript 继承很复杂, 比其它任何面向对象语言都复杂. Javascript 是少数使用 原型式继承 的语言之一, 这也正是 Javascript 的优点.
继承主要分为两种: 类继承, 原型式继承.
(一) 为什么要继承
代码重用, 易维护. 但是继承会增加对象之间的耦合度, 所以后面会讨论一些办法来对付它.
(二) 类式继承
模拟类式继承, 先给出一个父类
/* Class Person. */
function Person(name) {
this.name = name;
}
Person.prototype.getName = function(){
return this.name;
}
这个类作为下面例子的父类.
(1) 原型链 ( 我们现在讨论的是 类式继承 )
创建一个继承 Person 的类 Author :
/* Class Author. */
function Author(name, books) {
Person.call(this, name); // 1
this.books = books; // 2
}
//------- 分割线 ---------
Author.prototype = new Person(); // 3
Author.prototype.constructor = Author; // 4
Author.prototype.getBooks = function() { // 5
return this.books;
}
上面的第1句好理解, 像在Java里一样, 调用父类的构造函数. 有点说头的就是3 , 4句. 我在这里多做些关于 constructor, obj.prototype and [[Prototype]] 的笔记.
function Author() {
// some code goes here.
}
如图:
下面分析一下 内置属性 [[Prototype]], obj.prototype, constructor 这三个东西
首先明确一下叫法, [[Prototype]]指向的对象叫做对象的原型对象, prototype指向的对象叫做 prototype对象. 为什么要分开, 因为上图也显示了这两个对象是不同的.
我们知道 Javascript 中, 每个对象都有 原型对象, 但每个对象却不一定有 prototype 属性, 如果没有 prototype 属性, 自然也就不存在 prototype对象.
只有函数对象才有 prototype 这个属性, 如上图的 Author() .
当定义 Author ( function Author() {} ) 后, 上图的链式结构就已经存在了. 请特别注意一下 Author.prototype 对象的 constructor 属性, 它现在已经存在了, 并且指回 Author 记住这个.
* [[Prototype]] , prototype 区别及作用?
- [[Prototype]]指明了当前对象( Author )的原型对象, 是和 Author 真正相关的东西.. [[Prototype]] = 我的构造的 prototype 属性的值.
- 仔细想一下第一条, 正好说明了 prototype 属性的作用, 这个属性是用来 初值化 由 Author 构造出来的实例的 [[Prototype]] 属性.
假如有:
var patrick = new Author();
那么 patrick 的 [[Prototype]] 的值就是 Author.prototype 的值. 所以说, Author.prototype 属性与 Author 类更加息息相关, 为了好理解, 说的极端点 Author.prototype 是为其实例服务的, 和 Author 类本身关系不大.
// 创建一个实例
var patrick = new Author();
--------------- 回到上面话题 ---------------
/* Class Author. */
function Author(name, books) {
Person.call(this, name); // 1
this.books = books; // 2
}
//------- 分割线 ---------
Author.prototype = new Person(); // 3
Author.prototype.constructor = Author; // 4
Author.prototype.getBooks = function() { // 5
return this.books;
}
我们想让 Author 类继承 Person类, 设置Author.prototype对象为Person的一个实例就行, 因为手动改变了 Author.prototype , constructor也就不是不是原来的 Author() , 而变成了 Person(). 所以要手动把 constructor 设置回去, 设成 Author.
* 为什么 constructor 会变?
- 像第一个图里那样, constructor 是在创建Author类的时候在Author.prototype上自动创建的属性并指回Author. 当后来手动改变了Author.prototype = new Person() 后. 这时的
constructor 是什么? 假设new 出来的Person 为 p. 那么 查找 p.constructor , 在 p 里没有此属性, 沿着原型链往上找, p 的原型是 Person.prototype , 这里有 constructor 属性, 但是它的值是指向 Person() 构造函数. 所以要上面代码的第4句, 手动设置回成Author.
下一话题.
(2) extend 函数
给出一个extend() , 类似其它语言的 extend 关键字
/* Extend function */
function extend(subClass, superClass) {
var F = function() {};
F.prototype = superClass.prototype;
subClass.prototype = new F();
subClass.prototype.constructor = subClass();
}