原型链继承
子函数的原型指向父函数的实例
不足:引用类型的属性会被公用,比如下面的colors,多个子类继承,会被共享
//? 引用类型的属性会被公用,这个缺点不容忽视
function Father() {
this.name = 'father'
this.colors = [1,3]
}
Father.prototype.sayHi = function() {
console.log('Father say Hi~');
}
function Child() {}
Child.prototype = new Father()
let child = new Child()
let child2 = new Child()
child.sayHi();
child.colors.push(2)
console.log(child.colors) // [1,3,2]
console.log(child2.colors) // [1,3,2]
伪构造函数继承(经典继承)
通过call/apply来进行父函数的调用,使得可以复制一份父函数的内部属性
不足:方法必须写在构造函数内部,所以会引起相同函数的内存浪费,也就是没实例化一个函数,都会复制一份一模一样但是保存地址不一样的函数
function Father(name) {
this.name = name
this.colors = [1,2]
}
Father.prototype.sayHi = function() {
console.log('xxxx')
}
function Child(name) {
Father.call(this, name)
}
let child1 = new Child('xjx')
let child2 = new Child('ls')
child1.colors.push(333)
// child1.sayHi() // 会报错
console.log(child1.name) // xjx
console.log(child1.colors, child2.colors) // [1,2,333] [1,2]
组合继承
融合了原型链继承和经典继承,解决了引用类型和函数重复声明的问题
使用最多
不足:Father构造函数会被调用两次,导致实例和子函数的原型上都有属性,造成不需要的浪费和降低运行效率
function Father(name) {
this.name = name
this.colors = [1,2]
}
Father.prototype.sayHi = function() {
console.log('Father say hi~');
}
function Child(name) {
Father.call(this, name)
}
Child.prototype = new Father('xjx');
let child = new Child('ls')
let child1 = new Child('xx')
console.log(child.name) // ls
child.sayHi() // Father say hi~
child.colors.push(3)
console.log(child.colors, child1.colors) // [1,2,3] [1,2]
原型式继承
相当于ES5的Object.create()方法
传入的是个对象,所以相当于给prototype换了一个原型对象,所以跟原型继承很像,也是有引用类型的问题
不足:引用类型问题
function initialFn(o) {
function fn() {}
fn.prototype = o
return new fn()
}
let Obj = {
name: 'xjx',
colors: [1,3],
sayHi: function() {
console.log('father say hi~')
}
}
let child = initialFn(Obj)
let child2 = initialFn(Obj);
console.log(child.name) // xjx
child.colors.push('222')
child.sayHi() // father say hi~
console.log(child2.colors, child.colors) // [1,3,'222'] [1,3,'222']
寄生式继承
不足:
函数无法重用
引用类型问题仍然存在
function initialFn(o) {
let fn = Object(o);
fn.sayHi = function() {
console.log('father say hi~')
}
return fn;
}
let father = {
name: 'xjx',
colors: [1,3],
}
let child = initialFn(father)
let child1 = initialFn(father)
child.colors.push(111)
console.log(child.colors, child1.colors) // [1,3,111] [1,3,111]
child.sayHi()
寄生式组合继承
融合了组合式和寄生式的特点
解决组合式重复执行父级构造函数的问题,提高运行效率
解决了寄生式函数重用的问题
function Father(name) {
this.name = name
this.colors = [1,2]
}
Father.prototype.sayHi = function() {
console.log('father say hi~')
}
// 把内部属性拿到
function Child(name) {
Father.call(this, name);
}
// 把prototype上的属性拿到
function initialFn(fatherFn, childFn) {
// 保留父函数的副本
let prototype = Object(fatherFn.prototype);
// 还原子函数的构造函数指向
prototype.constructor = childFn;
childFn.prototype = prototype;
}
initialFn(Father, Child);
let child = new Child('xjx')
let child1 = new Child('ls')
child.colors.push(1122)
console.log(child.colors, child1.colors) // [1,2, 1122] [1,2]
child.sayHi() // father say hi~