1、原型链继承
让一个构造函数的原型是另一个构造函数的实例,那么这个构造函数new出来的实例就具有另一个实例的属性
// 父类构造函数
function Parent() {
this.info = {
name: '摩拉克斯',
age: 5000,
skill: '元素战技',
break: '元素爆发'
}
}
Parent.prototype.say = function () {
console.log(this.info)
}
// 子类构造函数
function Child() {}
// 将子类的原型指向父类的实例对象(原型链继承)
Child.prototype = new Parent()
// 实例化子类,发现子类本身是没有info属性和say方法的,但是可以读和写
let xiao = new Child()
xiao.info.name = '魈'
xiao.info.sex = '男'
xiao.say() // {name: '魈', age: 5000, skill: '元素战技', break: '元素爆发', sex: '男'}
// 但是当一个子类实例继承的属性方法更改,其他实例化的对象属性也会更改
let wangxiaomei = new Child()
wangxiaomei.info.sex = '女'
wangxiaomei.say() // {name: '魈', age: 5000, skill: '元素战技', break: '元素爆发', sex: '女'}
xiao.say() // {name: '魈', age: 5000, skill: '元素战技', break: '元素爆发', sex: '女'}
当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。
优点:写法方便简洁,容易理解。 缺点:对象实例共享所有继承的属性和方法。
2、借用构造函数继承
在子类型构造函数的内部调用父类型构造函数;使用 apply() 或 call() 方法将父对象的构造函数绑定在子对象上。
// 父类构造函数
function Parent({name, sex}) {
this.info = {
name: name,
age: 5000,
sex: sex
}
}
// 子类构造函数
function Child(obj) {
// 将父类构造函数指向子类的this
Parent.call(this, obj)
}
let xiao = new Child({name: '魈', sex: '男'})
xiao.info.age = 2000
console.log(xiao.info) // {name: '魈', age: 2000, sex: '男'}
let wangxiaomei = new Child({name: '王小美', sex: '女'})
wangxiaomei.info.age = 3000
console.log(wangxiaomei.info) // {name: '王小美', age: 3000, sex: '女'}
console.log(xiao.info) // {name: '魈', age: 2000, sex: '男'}
优点:解决了原型链实现继承的不能传参的问题和父类的原型共享的问题。 缺点:借用构造函数的缺点是方法都在构造函数中定义,因此无法实现函数复用。在父类型的原型中定义的方法,对子类型而言也是不可见的,结果所有类型都只能使用构造函数模式。
3、组合式继承
是前两种继承的结合,使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,又能够保证每个实例都有自己的属性
// 父类构造函数
function Parent({name, sex}) {
this.info = {
name: name,
age: 5000,
sex: sex
}
}
Parent.prototype.say = function () {
console.log(this.info)
}
// 子类构造函数
function Child(obj) {
// 将父类构造函数指向子类的this
Parent.call(this, obj)
}
// 将子类的原型指向父类的实例
Child.prototype = new Parent({name: '', sex:''})
// 既可以获取父类的属性,又可以调用父类的方法
let xiao = new Child({name: '魈', sex: '男'})
xiao.info.age = 2000
xiao.say() // {name: '魈', age: 2000, sex: '男'}
let wangxiaomei = new Child({name: '王小美', sex: '女'})
wangxiaomei.info.age = 3000
wangxiaomei.say() // {name: '王小美', age: 2000, sex: '女'}
xiao.say() // {name: '魈', age: 2000, sex: '男'}
优点就是解决了原型链继承和借用构造函数继承造成的影响。 缺点是无论在什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部
4、寄生组合式继承
减少组合继承的父类构造函数的一次调用,不直接调用父类构造函数给子类原型赋值,而是通过创建空函数 Fun 获取父类原型的副本
// 父类构造函数
function Parent({name, sex}) {
this.info = {
name: name,
age: 5000,
sex: sex
}
this.skill = '拒收病婿'
}
// 给父类添加方法
Parent.prototype.say = function () {
console.log(this.info)
}
// 子类构造函数
function Child(obj) {
// 将父类构造函数指向子类的this
Parent.call(this, obj)
this.skill = obj.skill
}
// 创建父类副本
function objectCopy(obj) {
function Fun() { }
Fun.prototype = obj
return new Fun()
}
// 创建副本,并且将子类的原型指向副本的实例
function inheritPrototype(child, parent) {
let prototype = objectCopy(parent.prototype)
prototype.constructor = child
Child.prototype = prototype
}
// 调用,继承
inheritPrototype(Child, Parent)
// 给子类添加方法
Child.prototype.saySkill = function () {
console.log(this.skill)
}
let xiao = new Child({name: '魈', sex: '男', skill: '靖妖傩舞' })
console.log(xiao)
xiao.say() // {name: '魈', age: 5000, sex: '男'}
xiao.saySkill() // 靖妖傩舞
注:自己学习记忆 学习自一位博主的文章