继承是面向对象中的重要部分,像 java、python 等语言都是用类来继承的,而 js 的继承是通过原型链来实现的。
原型链
通过将 prototype 指向对象的实例,来实现继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| function SuperType() { this.property = true }
SuperType.prototype.getSuperValue = function() { return this.property }
function SubType() { this.subproperty = false }
SubType.prototype = new SuperType()
SubType.prototype.getSubValue = function() { return this.subproperty }
var instance = new SubType() console.log(instance.getSuperValue())
|
缺点
所有 SubType 的实例都会继承同一个引用类型的对象,因为对于所有的 SubType 实例来说继承都是同一个对象的 colors
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function SuperType() { this.colors = ["red", "blue", "green"]; }
function SubType { }
SubType.prototype = new SuperType() var s1 = new SubType() s1.colors.push('black') console.log(s1)
var s2 = new SubType() console.log(s2)
|
借用构造函数
1 2 3 4 5 6 7 8 9 10 11
| function SuperType(name) { this.name = name }
function SubType() { SuperType.call(this, 'mink') this.age = 29 }
var instance = new SubType() console.log(instance.name, instance.age)
|
缺点
在超类原型中定义的方法,在子类中不可见
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function SuperType(name) { this.name = name }
SuperType.prototype.sayName = function() { console.log(this.name) }
function SubType() { SuperType.call(this, 'mink') this.age = 29 }
var instance = new SubType() console.log(instance.name, instance.age) instance.sayName()
|
组合继承
将原型链和借用构造函数组合在一起的继承方式叫组合继承
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 29
| function SuperType(name) { this.name = name this.colors = ['red', 'blue', 'green'] }
SuperType.prototype.sayName = function() { console.log(this.name) }
function SubType(name, age) { SuperType.call(this, name) this.age = age }
SubType.prototype = new SuperType()
SubType.prototype.sayAge = function() { console.log(this.age) }
var s1 = new SubType('mink', 29) s1.colors.push('black') s1.sayName() s1.sayAge()
var s2 = new SubType('mink2', 27) console.log(s2.colors) s2.sayName() s2.sayAge()
|
缺点
虽然组合继承解决了以上两个继承的问题,但是会调用两次构造方法
原型式
基于原有对象创建一个新的对象
1 2 3 4 5
| function object(o) { function F() {} F.prototype = o return new F() }
|
缺点
共享对象的引用类型
1 2 3 4 5 6 7 8
| var person = { friends: ['a', 'b', 'c'] }
var p1 = object(person) p1.friends.push('d')
console.log(p1.friends)
|
寄生式
用于创建一个封装继承的对象,新的对象继承原有对象的属性和方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function createAnother(original) { var clone = object(original) clone.sayHi = function() { console.log('hi') } return clone }
var person = { name: 'mink', age: '27' }
var another = createAnother(person) another.sayHi()
|
缺点
封装在函数内的方法无法重用
寄生组合式
超类中的属性通过构造的方式继承,而原型通过原型方式继承,解决了组合继承中超类构造两次的缺点
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
| function inheritPrototype(subtype, supertype) { var prototype = Object.create(supertype.prototype) prototype.constructor = subtype subtype.prototype = prototype }
function SuperType(name) { this.name = name this.colors = ['red', 'blue', 'green'] }
SuperType.prototype.sayName = function() { console.log(this.name) }
function SubType(name, age) { SuperType.call(this, name) this.age = age }
inheritPrototype(SubType, SuperType)
SubType.prototype.sayAge = function() { console.log(this.age) }
|