继承指的是可以让某个类型的对象获得另一个类型的对象的属性和方法. JavaScript本身并没有继承这一现有的机制, 下面看看我们是如何自己实现继承的.
类式继承
缺点
- 如果父类有引用类型的属性, 示例变量直接会相互影响
- 创建父类的时候, 无法向父类构造函数传递参数初始化
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| function superClass() { this.superValue = 'super' this.list = [1, 2, 3] }
superClass.prototype.getsuperValue = function () { return this.superValue }
function subClass() { this.subValue = 'sub' }
subClass.prototype = new superClass()
subClass.prototype.getsubValue = function () { return this.subValue }
var sub1 = new subClass() sub1.list.push(4) var sub2 = new subClass()
|
输出示例
1 2 3 4 5
| console.log(sub2.getsuperValue())
console.log(sub1.list) console.log(sub2.list)
|
构造函数继承
缺点
- 没有涉及到原型prototype, 所以父类的原型方法不会被子类继承
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| function superclass(id) { this.books = [1, 2, 3] this.id = id }
superclass.prototype.showBooks = function () { return this.books }
function subclass(id) { superclass.call(this, id) }
var sub1 = new subclass(10) sub1.books.push(4)
var sub2 = new subclass(20)
|
输出示例
1 2 3
| console.log(sub1.books, sub1.id) console.log(sub2.books, sub2.id) sub1.showBooks()
|
组合继承
缺点
- 在使用构造函数继承时执行了一遍父类的构造函数, 在实现子类原型的类式继承时又调用了一遍父类构造函数. 因此父类构造函数被调用了两遍
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| function superclass(name) { this.name = name this.books = [1, 2, 3] }
superclass.prototype.getName = function () { return this.name }
function subclass(name, time) { superclass.call(this, name) this.time = time }
subclass.prototype = new superclass()
subclass.prototype.getTime = function () { return this.time }
var sub1 = new subclass('js book', 200) sub1.books.push('java book') var sub2 = new subclass('ios book', 300)
|
输出示例
1 2 3 4
| console.log(sub1.books) console.log(sub2.books) console.log(sub1.getTime()) console.log(sub2.getName())
|
* 寄生组合式继承
在组合式继承中, 通过构造函数继承的属性和方法是没有问题的. 所以我们主要研究通过寄生式继承重新继承父类原型. 即 我们需要继承的仅仅是父类的原型, 不再需要调用父类的构造函数. 换句话说, 在构造函数继承中我们已经调用了父类的构造函数, 因此我们需要的就是父类的原型对象的一个副本, 而这个副本我们通过原型继承便可得到.
但是如果直接赋值给子类, 对父类原型对象复制得到的复制对象p中的constructor指向的不是subClass子类对象, 因此在寄生式继承中要对复制对象p做一次增强, 修复其constructor属性指向不正确的问题. 最后将得到的复制对象p赋值给子类的原型, 这样子类的原型就继承了父类的原型并且没有执行父类的构造函数
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| function inheritobject(o) { function F() {} F.prototype = o return new F() }
function inheritPrototype(subclass, superclass) { var p = inheritobject(superclass.prototype) p.constructor = subclass subclass.prototype = p }
|
寄生组合式继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| function superclass(name) { this.name = name this.colors = ['red', 'blue', 'green'] }
superclass.prototype.getName = function () { return this.name }
function subclass(name, time) { superclass.call(this, name) this.time = time }
inheritPrototype(subclass, superclass)
subclass.prototype.getTime = function () { return this.time }
var sub1 = new subclass('js book', 2014) sub1.colors.push('black') var sub2 = new subclass('css book', 2013)
|
输出示例
1 2 3 4
| console.log(sub1.colors) console.log(sub2.colors) console.log(sub2.getName()) console.log(sub2.getTime())
|
多继承
JavaScript中的继承时依赖于原型prototype链实现的, 只有一条原型链. 所以理论上是不能继承多个父类的. 然而JavaScript是灵活的, 通过一些技巧可以继承多个对象的属性来实现类似的多继承.这边的实现比较简单, 仅仅进行了值拷贝
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| var mix = function () { var i = 0 var len = arguments.length var arg for (; i < len; i++) { arg = arguments[i] for (const property in arg) { this[property] = arg[property] } } } Object.prototype.mix = mix function color() { this.color = '黑 白' } function animation() { this.food = '鱼' } function qie() { this.name = '企鹅' } let sub = new qie() sub.mix(new color(), new animation()) console.log(sub.color, sub.name, sub.food)
|