JS 实现继承常见的六种方式
一、原型链
先简单描述一下原型链,每个构造函数都有一个原型对象,原型对象又有一个 constructor 属性指向 构造函数,而每个构造函数实例对象都有一个 __proto__ 属性指向原型对象,如果当前实例队对象上没有这个属性,就会到它的原型对象中去找,而它的原型对象也是一个实例,就会重复之前步骤,直到找到 Object 的原型对象还没有找到,则返回 undefined。
function Parent() {
this.name = 'parent';
this.nums = [1, 2, 3];
}
function Child() {
this.type = 'child'
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
let c1 = new Child();
let c2 = new Child();
c1.nums.push(4);
console.log(c1, c2);
缺点:因为两个实例使用的是同一个原型对象。它们的内存空间是共享的,一个变化回引起另一个也发生变化。
二、构造函数继承
function Parent() {
this.name = 'parent';
this.nums = [1, 2, 3];
}
function Child() {
Parent.call(this);
this.type = 'child'
}
let c1 = new Child();
let c2 = new Child();
c1.nums.push(4);
console.log(c1, c2);
console.log(c1.getName());
缺点:无法访问到父类原型对象的属性,虽然解决了共享的问题。
三、组合继承
function Parent() {
this.name = 'parent';
this.nums = [1, 2, 3];
}
Parent.prototype.getName = function() {
return this.name;
}
function Child() {
Parent.call(this);
this.type = "child";
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
let c1 = new Child();
let c2 = new Child();
c1.nums.push(4);
console.log(c1, c2);
console.log(c1.getName());
缺点:虽然组合继承解决了前两个问题,但是 Parent 执行了两次,第一次是通过 call 方法,第二次改变 Child 是 prototype 的时候,而多执行一次就多了一次新能的开销
四、原型式继承
原型式继承就是同通过 ES5 里面的 Object.create 方法来实现,方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__Object.create()
这里我们来看一下普通对象是如何实现继承的。
let Parent = {
name: "parent",
nums: [1, 2, 3],
getName: function () {
return this.name;
},
};
let person1 = Object.create(Parent);
let person2 = Object.create(Parent);
person1.nums.push(4);
person1.name = "tom";
console.log(person1, person2);
console.log(person1.getName());
缺点:Object.create 属于浅拷贝,还是会存在内容共享的问题
五、寄生式继承
使用原型式继承可以获得一份目标对象的浅拷贝,然后利用这个浅拷贝的能力再进行增强,添加一些方法,这样的继承方式就叫作寄生式继承。
虽然其优缺点和原型式继承一样,但是对于普通对象的继承方式来说,寄生式继承相比于原型式继承,还是在父类基础上添加了更多的方法。
let Parent = {
name: "parent",
nums: [1, 2, 3],
getName: function () {
return this.name;
},
};
function clone(original) {
let clone = Object.create(original);
clone.getFriends = function () {
return this.friends;
};
return clone;
}
let p1 = new clone(Parent);
六、寄生组合式继承
结合前几种中提及的继承方式优缺点进行更改,这也是所有继承方式里面相对最优的继承方式。
function Parent() {
this.name = "parent";
this.play = [1, 2, 3];
}
Parent.prototype.getName = function () {
return this.name;
};
function Child() {
Parent.call(this);
this.friends = "child";
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
Child.prototype.getFriends = function () {
return this.friends;
};
let c1 = new Child();
let c2 = new Child();
c1.play.push(4);
console.log(c1.getName());
console.log(c1.getFriends());
console.log(c1, c2);