ES5的方式:
方式一:通过call调用父类构造函数
缺点:1. 该方法无法继承父类原型上的属性; 2. 父类构造函数多次调用,造成性能损失。
function Parent(name, age) {
this.name = name;
this.age = age;
}
function Child(name, age, sex) {
Parent.call(this, name, age);
this.sex = sex;
}
var c1 = new Child('c1', 18, 'male');
Parent.prototype.a = 'a';
console.log(c1); // {name: "c1", age: 18, sex: "male"}
方式二:通过父类原型继承
为了解决方式一的问题,将子类的原型对象直接修改为与父类原型一致,这样父类原型对象上的属性和方法子类也能继承。
function Parent(name, age) {
this.name = name;
this.age = age;
}
function Child(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
Child.prototype = Parent.prototype;
Parent.prototype.b = 'b'; // 给父类原型添加一个属性
var c1 = new Child('c1', 18, 'male');
console.log(c1); (1)
Child.prototype.a = 'a'; (3) // 给子类原型添加属性
var p1 = new Parent('p1', 38);
console.log(p1); (2)
(1)和(2)的执行结果如下:
由上图可以看出,子类确实继承到了父类的原型上的属性b,但是(3)处,当子类的原型属性被修改时,父类的原型属性也发生了变化,这显然是不行的,因为父类被添加上了原本不需要的属性。
方式三:通过new父类构造函数的方式
function Parent(name, age) {
this.name = name; this.age = age;
}
function Child(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
Child.prototype = new Parent();
Parent.prototype.b = 'b'; // 给父类原型添加一个属性
var c1 = new Child('c1', 18, 'male');
console.log(c1); (1)
Child.prototype.a = 'a'; // 给子类原型添加属性
var p1 = new Parent('p1', 38);
console.log(p1); (2)
console.log(Child.prototype.constructor); // Parent(name, age){ ... }
(1) 和 (2)处的执行结果如下:
这种方法的第一个缺点是Child的构造函数指向了Parent(){};第二是如果父类的属性是引用类型,修改子类的某个实例的该属性时,其他实例都会受到影响,如下:
function Parent(name, age) {
this.name = name;
this.age = age;
this.play = [1, 2, 3];
}
function Child(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
Child.prototype = new Parent();
var c1 = new Child('c1', 18, 'male');
console.log(c1.play); // [1, 2, 3]
var c2 = new Child('c2', 18, 'male');
console.log(c2.play); // [1, 2, 3]
c2.play.push(4);
console.log(c1.play); // [1, 2, 3, 4]
console.log(c2.play); // [1, 2, 3, 4]
方式四:组合继承方式
function Parent(name, age) {
this.name = name;
this.age = age;
this.play = [1, 2, 3];
}
function Child(name, age, sex) {
Parent.call(this, name, age);
this.sex = sex;
}
Child.prototype = Parent.prototype;
Child.prototype.constructor = Child; // 修正构造函数指向
var c1 = new Child('c1', 18, 'male');
console.log(c1.play); // [1, 2, 3]
var c2 = new Child('c2', 18, 'male');
console.log(c2.play); // [1, 2, 3]
c2.play.push(4);
console.log(c1.play); // [1, 2, 3]
console.log(c2.play); // [1, 2, 3, 4]
这种方案是用得比较多的一种方式,不过依然存在修改子类原型对象,父类原型对象也会受到影响的问题。
方式五:组合继承优化1
let Parent = (function () {
function Parent (name) {
this.name = name || 'Parent';
this.a = 'a';
this.sleep = function(){
console.log(this.name + '正在睡觉!');
}
}
Parent.prototype.eat = function(food) {
console.log(this.name + '正在吃:' + food);
}
return Parent;
})()
let Child = (function () {
function Child(name){
Parent.call(this, name);
}
(function(sub, sup){
var Super = function(){};
Super.prototype = sup.prototype;
sub.prototype = new Super();
sub.prototype.constructor = sub
})(Child, Parent);
Child.prototype.b = 'b';
return Child;
})()
这种方式是比较完美的继承方式,但是依然存在多次调用父类构造函数的问题。