function BaseClass(name) {
this.name = name
this.arr = [1, 2]
}
function ChildClass() {}
ChildClass.prototype = new BaseClass()
var child1 = new ChildClass()
var child2 = new ChildClass()
console.log(child1.name) // undefined
console.log(child2.name) // undefined
child1.arr.push(3)
console.log(child1.arr) // [1, 2, 3]
console.log(child2.arr) // [1, 2, 3]
缺点:
- 父类构造器中的引用类型属性被实例所共享
- 不能向父类传参
function BaseClass(name) {
this.name = name
this.arr = [1, 2]
}
function ChildClass(name, age) {
BaseClass.call(this, name)
this.age = age
}
var child1 = new ChildClass('bob', 20)
var child2 = new ChildClass('kyire', 18)
console.log(child1.name) // bob
console.log(child1.arr) // [1, 2]
console.log(child1.age) // 20
child1.arr.push(5) // [1, 2, 5]
console.log(child2.name) // kyire
console.log(child2.arr) // [1, 2]
console.log(child2.age) // 18
优点:
- 避免了 父类构造器中的引用类型属性被实例所共享问题
- 解决了 不能向父类传参 问题
缺点:
- 方法都在构造函数中定义,每次创建实例都会创建一遍方法, 无法做到函数复用
function BaseClass(name) {
this.name = name
this.arr = [1, 2]
}
BaseClass.prototype.getName = function () {
console.log(this.name)
}
function ChildClass(name, age) {
BaseClass.call(this, name) // 注2
}
ChildClass.prototype = new BaseClass() // 注1
ChildClass.prototype.constructor = ChildClass
var child1 = new ChildClass('bob', 18)
var child2 = new ChildClass('kyire', 20)
child1.arr.push(10)
console.log(child1.name) // bob
console.log(child1.arr) // [1, 2, 10]
child1.getName() // bob
console.log(child2.name) // kyire
console.log(child2.arr) // [1, 2]
child2.getName() // kyire
console.log(child1.getName === child2.getName) // true
结合了原型链继承和构造函数的优点,是 JavaScript 中最常用的继承模式
注1: 在 此位置 第一次 掉用了 BaseClass 父构造函数, 此时 将 BaseClass 的实例属性 添加到了 ChildClass 的原型上
注2: 在此位置 又一次 调用了 BaseClass 父构造函数 , 此时 将 BaseClass 的实例属性 添加到了ChildClass 实例上 , 使之覆盖 原型上的 同名属性
function createObj(obj) {
function Base() {}
Base.prototype = obj
return new Base()
}
这也是 object.create 的模拟实现
这种继承的缺点和 原型链继承的缺点一样
var person = {
name: 'bob',
likeArr: ['sports', 'music']
}
var child1 = createObj(person)
var child2 = createObj(person)
console.log(child1.likeArr === child2.likeArr) // true
创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象
var person = {
name: 'bob',
likeArr: ['sports', 'music']
}
function createObj(o) {
let clone = Object.create(o)
clone.sayName = function() {
console.log(this.name)
}
}
var child1 = createObj(person)
var child2 = createObj(person)
console.log(child1.likeArr === child2.likeArr) // true
console.log(child1.sayName === child2.sayName) // false
使用寄生模式来为对象添加函数, 由于不能做到函数复用而降低效率 , 和构造函数模式类似
为了解决 组合式继承的 两次调用 父构造函数 带来的 效率问题 和 属性覆盖问题 (见 组合继承 的 注1 和 注2)
思路 : 因为 子类只需要继承 父类的原型 , 不必把 父类的 实例属性 放在 子类的原型上
function BaseClass(name) {
this.name = name
this.arr = [1, 2]
}
BaseClass.prototype.getName = function () {
console.log(this.name)
}
function ChildClass(name, age) {
BaseClass.call(this, name)
}
function inheritPrototype(child, parent) {
var proto = Object.create(parent.prototype)
proto.constructor = child
child.prototype = proto
}
// 此处 解决了两次调用 BaseClass 问题
// 也解决了 name arr 属性覆盖问题
inheritPrototype(ChildClass, BaseClass)
// ChildClass.prototype = new BaseClass()
// ChildClass.prototype.constructor = ChildClass
var child1 = new ChildClass('bob', 18)
var child2 = new ChildClass('kyire', 20)
child1.arr.push(10)
console.log(child1.name) // bob
console.log(child1.arr) // [1, 2, 10]
child1.getName() // bob
console.log(child2.name) // kyire
console.log(child2.arr) // [1, 2]
child2.getName() // kyire
console.log(child1.getName === child2.getName) // true