- 原型链继承
// 父类
function Person() {}
// 子类
function Student(){}
// 继承
Student.prototype = new Person()
我画张图就很容易理解了, (画错了, 最下边两个应该是Student实例)
缺点, 共享prototype上的属性,如果是引用类型, 就会造成多个实例共享该属性, GG
- 构造函数继承
// 父类
function Person(name) {
this.say = function() {} // 改动的代码
}
// 子类
function Student(name){
Person.call(this,name)
}
在Student中调用父类构造函数, 因为父类前面没有用new, 所以只是简单执行, 然后把this指向了student实例, 相当于
// 父类
function Person(name) {
this.say = function() {} // 改动的代码
}
// 子类
function Student(name){
this.say = function() {} // 父类构造函数里面的代码
}
所以该模式叫借用构造函数模式
缺点:每实例化一个子类, 就要执行一次父类的构造函数, 所以子类所有实例上相同的属性都是拷贝值
- 组合继承
// 父类
function Person() {
this.hobbies = ['music','reading']
}
// 父类函数
Person.prototype.say = function() {console.log('I am a person')}
// 子类
function Student(){
Person.call(this) // 构造函数继承(继承属性)
}
// 继承
Student.prototype = new Person() // 原型链继承(继承方法)
缺点: 父类构造函数里面的代码会执行2遍
把共享方法挂在父类构造函数里, 方便拷贝值而不互相影响, 把共享方法定义在父类构造函数上, 用原型方式继承
- 原型式继承
该方式其实是根据一个已有的实例去继承, 将该实例或该实例的原型作为原型
function clone (proto) {
function F () {}
F.prototype = proto
return new F()
}
function Parent(){
this.arr = [1,2,3]
}
let obj = new Parent()
let aa = clone(obj)
let bb = clone(obj)
aa.length = 4
console.log(bb.arr) // [1,2,3,undefined]
看图,
参数可以传入一个对象, 相当于情况1, 情况1跟原型链继承是类似的
如果传入的是原型对象, 相当于情况2, Object.create(xx.prototype)
缺点, 也会造成引用属性共享
5. 寄生式继承
寄生式继承其实就是在原型式继承的基础上,做了一些增强.
跟构造函数模式一样, 实例对象的引用类型属性无法共享, say方法会在每个实例上创建一次
function cloneAndStrengthen(proto){
function F () {}
F.prototype = proto
let f = new F()
f.say = function() {
console.log('I am a person')
}
return f
}
- 寄生组合式继承
寄生组合式继承就是为了降低组合模式中, 调用父类构造函数的开销而出现的
其背后的基本思路是: 不必为了指定子类型的原型而调用超类型的构造函数
寄生组合式到底有什么优点?为什么要新建一个F函数, 再创建F的实例?先看图:
回想组合式继承, 会执行两次父类构造函数
第一次是将子类的原型指向父类的实例
第二次是子类实例化的时候, 借用了父类的构造函数
寄生组合为什么要新建一个F构造函数,再将Child的原型指向F实例?
个人理解继承组合和组合继承有不同的使用场景, 在于组合继承,子类会带有父类构造函数中this给的方法、属性, 而寄生组合继承, 拿的是F的实例,而F和Parent不同在于F是个空函数
function inherit(sub, super){
let prototype = clone(super.prototype)
// function F () {}
// F.prototype = proto
// return new F()
prototype.constructor = sub
sub.prototype = prototype
}