JS继承的几种实现方式
首先,我们定义一个父类:
function Person(myName, myAge) {
this.name = myName;
this.age = myAge;
this.arr = [1,2];
this.say = function () {
console.log(this.name, this.age);
}
}
Person.prototype.eat = function () {
console.log("eat");
};
方式一(原型链继承):
将父类的实例对象作为子类的原型对象
function Student(myName, myAge, myScore) {
this.score = myScore;
this.study = function () {
console.log("day day up");
}
}
Student.prototype = new Person();
Student.prototype.constructor = Student;
let stu1 = new Student();
let stu2 = new Student();
stu1.arr.push(3);
console.log(stu2.arr); // [1,2,3]
缺点:
- 创建子类实例时,无法向父类构造函数传参
- 来自子类的原型对象中的属性被所有实例共享,导致引用类型的属性会被多个实例修改
方式二(借用构造函数继承):
将Person的this指向Student的实例对象stu,解决了传参问题
function Student(myName, myAge, myScore) {
Person.call(this, myName, myAge);
this.score = myScore;
this.study = function () {
console.log("day day up");
}
}
let stu = new Student("ww", 19, 99);
console.log(stu.score); // 99
stu.say(); // ww 19
stu.eat(); // 报错:不存在这个函数
缺点:
- 只能继承父类的实例属性和方法,不能继承原型属性/方法
- 本质上是复制父类的实例属性和方法给子类,两个函数没有关系,没有实现函数的复用,性能不高
方式三(组合继承):
组合继承是方式一和方式二的结合,原型链实现对原型属性和方法的继承,借用构造函数来实现实例属性和方法的继承。
function Student(myName, myAge, myScore) {
Person.call(this, myName, myAge);
this.score = myScore;
this.study = function () {
console.log("day day up");
}
}
Student.prototype = new Person();
Student.prototype.constructor = Student;
这是一种比较常用的继承方式,缺点是调用了两次父类的构造函数,使得子类的实例对象和原型对象中有两份相同的属性和方法
方式四(寄生组合继承):
使用Object.create()替代new
function Student(myName, myAge, myScore) {
Person.call(this, myName, myAge);
this.score = myScore;
this.study = function () {
console.log("day day up");
}
}
var prototype = Object.create(Person.prototype); //创建对象
prototype.constructor = Student; //增强对象
Student.prototype = prototype; //指定对象
通过Object.create()创建的函数,并没有去调用构造函数,因此构造函数上的属性和方法不会继承到Object.create创建的实例中,只有原型对象中的属性和方法。解决了组合继承中调用两次父类的构造函数的问题
方式五(ES6继承):
简化了代码,实现原理同寄生组合继承
Class Student extends Person{
Constructor(myName, myAge, myAge){
this.score = myScore;
super(myName, myAge);
}
}