ES5的继承
我个人所理解的js中的继承, 实现继承的方式就是将子类的prototype的__proto__指向
父类的prototype且子类的prototype的constructor指向子类本身
为什么我会有这种理解, 推断如下, 首先我们都知道js中Object构造函数
是所有构造函数的超类, 如图所示

通过类比, 那么我们实现继承的最终目标就明确了! 超类为Animal, 子类为Dog, 只要将
两者的关系实现如下图所示, 也就实现了Dog继承了Animal。

function Animal () {
}
Animal.prototype.__proto__ === Object.prototype
javascript高级程序设计中提到了六种实现继承的方式, 每种方式各有千秋, 都有其优缺点
原型链继承
缺点
- 无法向超类传递参数
- 由于构造函数的原型指向了同一个对象, 当为引用类型的对象时, 实例改变了引用类型的值,
可能会导致后面的
new出来的实例与期望不一致
function Animal () {
this.name = 'animal'
this.colors = ['red', 'blue', 'black']
}
function Dog () {
this.speak = 'bark'
}
// 缺点1
Dog.prototype = new Animal() // 继承的关键步骤
// { speak: 'bark', __proto__: { name: 'animal', colors: ["red", "blue", "black"] } }
var dog1 = new Dog();
// 缺点2
// { speak: 'bark', __proto__: { name: 'animal', colors: ["red", "blue", "black", "white"] } }
dog1.colors.push('white')
// { speak: 'bark', __proto__: { name: 'animal', colors: ["red", "blue", "black", "white"] } }
var dog2 = new Dog();
借用构造函数
优点
- 解决了原型链继承无法向超类传参的问题
- 属性中的引用对象也不会被修改
缺点
- 方法都在构造函数中定义, 函数复用性差 (此缺点我无法理解, 有厉害的掘友麻烦解释下)
- 实例无法获取超类原型上的属性
function Animal (name) {
this.name = name
this.colors = ['red', 'blue', 'black']
}
Animal.prototype.sayName = function () {
console.log(this.name)
}
// 优点1
function Dog (name) {
this.speak = 'bark'
Animal.call(this, name)
}
var dog1 = new Dog('happy')
// 优点2
dog1.colors.push('white')
var dog2 = new Dog('sad')
// 缺点2
dog1.sayName // undefined
组合继承
优点
- 解决了借用构造函数法无法继承超类原型链上属性的问题
缺点
- 超类调用了两次, 函数的自身属性和原型上会有两份表现相同(不是同一个引用)的属性
function Animal (name) {
this.name = name
this.colors = ['red', 'blue', 'black']
}
Animal.prototype.sayName = function () {
console.log(this.name)
}
function Dog (name) {
this.speak = 'bark'
Animal.call(this, name)
}
Dog.prototype = new Animal()
Dog.prototype.constructor = Dog
var dog1 = new Dog('test')
// 优点1
dog1.sayName // ƒ () { console.log(this.name) }
// 缺点1
delete dog1.colors
dog1.colors // ["red", "blue", "black"] 属性依然存在
原型式的继承
缺点
- 每次都需要重新创建一个函数
- 当传入的对象中包含引用类型的值时, 可能会改变其值
function object(o) {
// 缺点1
function F () {}
F.prototype = o
return new F()
}
const obj = { name: 'test', colors: ['red', 'blue', 'black'] }
var o1 = object(obj)
var o2 = object(obj)
o1.colors.push('white')
o2.colors // ["red", "blue", "black", "white"]
以上就是原型式的继承方式, 后来es5中提出了Object.create()将该继承方式规范化
寄生式的继承
个人感觉这种继承方式使用范围不广, 一笔带过
在主要考虑对象也不是自定义类型和构造函数的情况下, 寄生式继承也是一种有用的方式, 使用object 并不是必须的, 任何能够返回此对象的都适用于此模式。
function createObject(o) {
let clone = object(o)
clone.sayName = function () {
console.log(this.name)
}
return clone
}
寄生组合式继承
优点
- 只调用了一次超类的构造函数, 避免了创建多余的属性
- 原型链能够保持不变
function extends (parentClass, childClass) {
var prototype = Object.create(parentClass.prototype)
childClass.prototype = prototype
childClass.prototype.constructor = childClass
}