继承是面向对象中一个比较核心的概念,其他正统面相对象语言都会用两种方式实现继承:一个是接口;一个是继承。而ECMAScript只支持继承,不支持接口实现,而实现继承的方式需要依靠原型链来完成。
# 原型链继承
```
function Box () { // 被继承的函数叫做超类(父类、基类)
this.name = 'Lee'
}
function Desk () { // 继承的函数叫做子类(派生类)
this.age = 100
}
Desk.prototype = new Box() // Desk继承了Box,通过原型,形成链条。子类的原型指向父类的实例
var desk = new Desk()
desk.age
desk.name // 得到被继承的属性
```
使用原型链继承会存在以下两个问题:
1. 字面量重写原型会中断关系,使用引用类型的原型;
2. 子类无法给超类传递参数。
# 借用构造函数(对象冒充)继承
为了解决引用共享和超类无法传参的问题,我们采用一种叫借用构造函数的技术,或者称为对象冒充(伪造对象、经典继承)的技术来解决这两个问题。
```
function Box (name, age) {
this.name = name
this.age = age
}
Box.prototype.family = '家庭'
function Desk (name, age) {
Box.call(this, name, age) // 对象冒充,给超类传参
}
var desk = new Desk('Lee', 100)
desk.name // Lee
desk.age // 100
desk.family // undefined 对象冒充只能继承实例(构造函数)属性或方法,不能继承类(原型)属性或方法
```
# 组合继承
借用构造函数虽然解决了刚才两种问题,但是没有原型,复用则无从谈起。所以,我们需要原型链+借用构造函数的模式,这种模式称为组合继承。
```
function Box (age) {
this.age = age
}
Box.prototype.run = function () {
return '年龄是:' + this.age
}
function Desk (age) {
Box.call(this, age) // 对象冒充
}
Desk.prototype = new Box()
var desk = new Desk(100)
desk.run()
```
# 原型式继承
原型式继承借助原型并给予已有的对象创建新对象,同时不必因此创建自定义类型。
```
function obj (o) { // 创建一个字面量函数,临时中转函数
function F () {} // 一个临时新建的对象(构造函数),用来存储传递进来的对象
F.prototype = o // 把字面量函数赋值给构造函数的原型
return new F()
}
// 字面量 相当于 var box = new Box()
var box = {
name: 'Lee',
age: 100
}
var box1 = obj(box)
```
# 寄生式继承(原型式+工厂模式)
目的是为了封装创建对象的过程
```
/**
临时中转函数
*/
function obj (o) { // 创建一个字面量函数,临时中转函数
function F () {} // 一个临时新建的对象(构造函数),用来存储传递进来的对象
F.prototype = o // 把字面量函数赋值给构造函数的原型
return new F()
}
/**
寄生函数
*/
function create (o) {
var f = obj(o)
// 此处可以对f进行扩展,例如下面给f扩展一个run函数
f.run = function () {
return this.name
}
return f
}
// 字面量 相当于 var box = new Box()
var box = {
name: 'Lee',
age: 100
}
var box1 =create(box)
```
# 寄生组合继承
组合式继承是javascript最常用的继承模式,但是组合式继承也有一点小问题,就是超类在使用过程中会被调用两次:
一次是创建子类的时候;
一次是在子类构造函数的内部
```
function Box (age) {
this.age = age
}
Box.prototype.run = function () {
return '年龄是:' + this.age
}
function Desk (age) {
Box.call(this, age) // 第二次调用Box
}
Desk.prototype = new Box() // 第一次调用Box
var desk = new Desk(100)
desk.run()
```
以上带面是之前的组合式继承,那么寄生组合继承,解决了两次调用的问题
```
function obj (o) {
function F () {}
F.prototype = o
return new F()
}
function create (box, desk) {
var f = obj(box.prototype)
f.constructor = desk
desk.prototype = f
}
function Box (name, age) {
this.name = name
this.age = age
}
Box.prototype.run = function () {
return this.name + this.age
}
function Desk (name, age) {
Box.call(this, name, age)
}
// 通过寄生组合继承来实现继承
create(Box, Desk) // 这句话用来替代Desk.prototype = new Box()
var desk = new Desk()
desk.run()
```