经典继承
该继承方式综合了原型链和构造函数的方式,将两者的优点集中了起来。组合继承弥补了之前原型链和盗用构造函数这两种方式各自的不足,是 JavaScript 中使用最多的继承方式。
组合继承最大的问题就是效率问题。最主要就是父类的构造函数始终会被调用两次:一次是在创建子类原型时调用,另一次是在子类构造函数中调用。本质上,子类原型最终是要包含超类对象的所有实例属性,子类构造函数只要在执行时重写自己的原型就行了。
下面是一个组合继承的例子:
基类
var Person = function (name, age) {
this.name = name;
this.age = age;
}
Person.prototype.test = "this is a test";
Person.prototype.testFunc = function () {
console.log('this is a testFunc');
}
子类
var Student = function (name, age, gender, score) {
Person.apply(this, [name, age]); // 盗用构造函数
this.gender = gender;
this.score = score;
}
Student.prototype = new Person(); // 改变 Student 构造函数的原型对象
Student.prototype.testStuFunc = function () {
console.log('this is a testStuFunc');
}
测试
var zhangsan = new Student("张三", 18, "男", 100);
console.log(zhangsan.name); // 张三
console.log(zhangsan.age); // 18
console.log(zhangsan.gender); // 男
console.log(zhangsan.score); // 100
console.log(zhangsan.test); // this is a test
zhangsan.testFunc(); // this is a testFunc
zhangsan.testStuFunc(); // this is a testStuFunc
圣杯继承
圣杯模式的继承解决了组合继承的一些问题,其基本思路就是不通过调用父类构造函数来给子类原型赋值,而是取得父类原型的一个副本,然后将返回的新对象赋值给子类原型。
下面是圣杯模式的示例:
// target 是子类,origin 是基类
// target ---> Student, origin ---> Person
function inherit(target, origin) {
function F() { }; // 没有任何多余的属性
// origin.prototype === Person.prototype, origin.prototype.constructor === Person 构造函数
F.prototype = origin.prototype;
// 假设 new F() 出来的对象叫小 f
// 那么这个 f 的原型对象 === F.prototype === Person.prototype
// 那么 f.constructor === Person.prototype.constructor === Person 的构造函数
target.prototype = new F();
// 而 f 这个对象又是 target 对象的原型对象
// 这意味着 target.prototype.constructor === f.constructor
// 所以 target 的 constructor 会指向 Person 构造函数
// 我们要让子类的 constructor 重新指向自己
// 若不修改则会发现 constructor 指向的是父类的构造函数
target.prototype.constructor = target;
}
基类
var Person = function (name, age) {
this.name = name;
this.age = age;
}
Person.prototype.test = "this is a test";
Person.prototype.testFunc = function () {
console.log('this is a testFunc');
}
子类
var Student = function (name, age, gender, score) {
Person.apply(this, [name, age]);
this.gender = gender;
this.score = score;
}
inherit(Student, Person); // 使用圣杯模式实现继承
// 在子类上面添加方法
Student.prototype.testStuFunc = function () {
console.log('this is a testStuFunc');
}
测试
var zhangsan = new Student("张三", 18, "男", 100);
console.log(zhangsan.name); // 张三
console.log(zhangsan.age); // 18
console.log(zhangsan.gender); // 男
console.log(zhangsan.score); // 100
console.log(zhangsan.test); // this is a test
zhangsan.testFunc(); // this is a testFunc
zhangsan.testStuFunc(); // this is a testStuFunc