繼承是面向對象中一個比較核心的概念,其他正統面相對象語言都會用兩種方式實現繼承:一個是接口;一個是繼承。而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()
```