原型链继承
最常见的继承方式,涉及构造函数,原型和实例
- 每一个构造函数都有一个原型对象
- 原型对象又包含一个指向构造函数的指针
而实例则包含一个原型对象的指针
这种继承方式存在问题function Parent(){ this.name="parent" this.own = [1,2,3] } function Child(){ this.type ="child" } Child.prototype = new Parent() console.log(new Child()) // Parent { type: 'child' } let c1 = new Child(); let c2 = new Child(); c1.own.push(4) console.log(c1.own,c2.own) // [ 1, 2, 3, 4 ] [ 1, 2, 3, 4 ]
因为两个实例使用同一个原型对象,内存空间共享,当一个发生变化的时候,另外一个也随之进行了变化
构造函数继承
借助call
实现
function Parent(){
this.name="parent"
this.own = [1,2,3]
}
Parent.prototype.getName = function () {
return this.name;
}
function Child(){
Parent.call(this)
this.type ="child"
}
const child = new Child()
console.log(child) // Child { name: 'parent', own: [ 1, 2, 3 ], type: 'child' }
console.log(child.getName()) // child.getName is not a function
这种方式解决了原型共享内存的弊端,随之也带来了新的问题。只能继承父类的实例属性和方法,不能继承原型属性或者方法。
组合继承
这种方式结合了前两种继承方式的优缺点,结合起来的继承
function Parent(){
this.name="parent"
this.own = [1,2,3]
}
Parent.prototype.getName = function () {
return this.name;
}
function Child(){
// 第二次调用
Parent.call(this)
this.type ="child"
}
// 第一次调用 Parent3()
Child.prototype = new Parent()
// 手动挂上构造器,指向自己的构造函数
Child.prototype.constructor = Child;
let c1 = new Child()
let c2 = new Child()
c1.own.push(4)
console.log(c1.own,c2.own) // [ 1, 2, 3, 4 ] [ 1, 2, 3 ]
console.log(c1.getName()) // parent
console.log(c2.getName()) // parent
Parent执行了两次,第一次是改变Child 的 prototype 的时候,第二次是通过 call 方法调用 Parent 的时候,那么 Parent 多构造一次就多进行了一次性能开销
原型式继承
使用ES5 里面的 Object.create
方法,这个方法接收两个参数:一是用作新对象原型的对象、二是为新对象定义额外属性的对象(可选参数)
let parent = {
name:"parent",
friend:['1',',2','3'],
getName:function(){
return this.name
}
}
let p1 = Object.create(parent)
p1.name ="jake"
p1.friend.push("a")
let p2 = Object.create(parent)
p1.friend.push("b")
console.log(p1.name) // jake
console.log(p1.name === p1.getName()) // true
console.log(p2.name) // parent
console.log(p1.friend) // [ '1', ',2', '3', 'a', 'b' ]
console.log(p2.friend) // [ '1', ',2', '3', 'a', 'b' ]
这种继承方式的缺点很明显,多个实例的引用类型属性指向相同的内存,存在篡改的可能
寄生式继承
原型式继承可以获得一份目标对象的浅拷贝,然后利用这个浅拷贝的能力再进行增强,添加一些方法,这样的继承方式就叫作寄生式继承
优缺点和原型式继承一样,但是对于普通对象的继承方式来说,寄生式继承相比于原型式继承,在父类基础上添加了更多的方法
let parent = {
name:"parent",
friend:['1',',2','3'],
getName:function(){
return this.name
}
}
function clone(src) {
let clone = Object.create(src);
clone.getFriends = function() {
return this.friend;
};
return clone;
}
let p1 =clone(parent)
console.log(p1.getName()) // parent
console.log(p1.getFriends()) // [ '1', ',2', '3' ]
增加了 getFriends 的方法,从而使 p1 这个普通对象在继承过程中又增加了一个方法,依然存在两次调用父类的构造函数造成浪费
寄生组合式继承
解决前几种继承方式的缺点,较好地实现了继承想要的结果,同时也减少了构造次数,减少了性能的开销
function clone(parent, child) {
// 这里改用 Object.create 就可以减少组合继承中多进行一次构造的过程
child.prototype = Object.create(parent.prototype)
child.prototype.constructor = child;
}
function Parent() {
this.name = "parent"
this.friend = ['1', ',2', '3']
}
Parent.prototype.getName = function () {
return this.name
}
function Child() {
Parent.call(this)
this.friend = "labor"
}
clone(Parent, Child)
Child.prototype.getFriends = function(){
return this.friend
}
let c1 = new Child()
console.log(c1) // Child { name: 'parent', friend: 'labor' }
console.log(c1.getName()) // parent
console.log(c1.getFriends()) // labor
ES6中的extends
也采用这种方式
归类
不使用Object.create
的方式构造函数继承,原型链继承,他们组合成为组合继承
使用Object.create
的方式原型式继承,寄生式继承,依此为基础有了寄生组合式继承,和ES6 extends
类似