JavaScript 面向对象 —— 继承
作者:Seiya
时间:2019年07月29日
前言
上篇笔记介绍了如何“封装”数据和方法,以及如何从原型对象生成实例。这篇笔记主要介绍对象之间的继承。
原型链继承
// 将 Cat 的 prototype 对象指向一个 Animal 的实例
Cat.prototype = new Animal();
// 修改 Cat.prototype.constructor 指向 Animal 而不是 Cat ,否则会导致继承链紊乱
Cat.prototype.constructor = Cat;
var cat1 = new Cat('大毛', '黄色');
alert(cat1.species); // 动物
缺点
不能向父类的构造函数中传递参数;
父类中的引用类型属性会被实例共享;
需要修正实例的 constructor 指向,否则会指向父类;
构造函数继承
使用 call 或 apply 方法,将父对象的构造函数绑定在子对象上,如下所示:
function Cat(name, color) {
Animal.apply(this, arguments);
this.name = name;
this.color = color;
}
var cat1 = new Cat('大毛', '黄色');
alert(cat1.species); // 动物
优点
子类可以向父类的构造函数中传参,子类实例中的引用类型属性互不干扰。
缺点
子类实例无法访问父类的原型(无法复用父类原型中的方法)
组合继承
为解决纯原型继承不能给父传参和纯构造函数继承不能继承父类原型的缺点,把二者结合起来实现,如下所示:
function Animal() {}
// 不变的属性都可以直接写入 Animal.prototype
Animal.prototype.species = '动物';
function Cat(name, color) {
Animal.apply(this, arguments);
this.name = name;
this.color = color;
}
Cat.prototype = Animal.prototype;
Cat.prototype.constructor = Cat;
var cat1 = new Cat('大毛', '黄色');
alert(cat1.species); // 动物
缺点
与前一种方法相比,这样做的优点是效率比较高(不用执行和建立 Animal 的实例了),比较省内存。缺点是 Cat.prototype 和 Animal.prototype 现在指向了同一个对象,那么任何对 Cat.prototype 的修改,都会反映到 Animal.prototype。
原型式继承
var F = function() {};
F.prototype = Animal.prototype;
Cat.prototype = new F();
Cat.prototype.constructor = Cat;
这时,修改 Cat 的 prototype 对象,就不会影响到 Animal 的 prototype 对象。
缺点
只继承了父类的原型,子类实例化时一样不能给父类的构造函数传参
寄生式继承
寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象。
function Cat(object) {
var clone = Object(object); // 通过调用函数创建一个新对象
clone.species = '动物'; // 以某种方式来增强这个对象
return clone; // 返回这个对象
}
寄生组合式继承
所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。其背后的基本思路是:不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型原型的一个副本而已。
function Cat(object, superObject) {
var prototype = Object(superObject.prototype); // 创建对象
prototype.constructor = object; // 增强这个对象
object.prototype = prototype; // 指定对象
}
拷贝继承
纯粹采用"拷贝"方法实现继承,简单说,把父对象的所有属性和方法,拷贝进子对象:
function Animal() {}
Animal.prototype.species = '动物';
function extend2(Child, Parent) {
var p = Parent.prototype;
var c = Child.prototype;
for (var i in p) {
c[i] = p[i];
}
c.uber = p;
}
extend2(Cat, Animal);
var cat1 = new Cat('大毛', '黄色');
alert(cat1.species); // 动物
深拷贝
所谓"深拷贝",就是能够实现真正意义上的数组和对象的拷贝。它的实现并不难,只要递归调用"浅拷贝"就行了。
function deepCopy(p, c) {
var c = c || {};
for (var i in p) {
if (typeof p[i] === 'object') {
c[i] = p[i].constructor === Array ? [] : {};
deepCopy(p[i], c[i]);
} else {
c[i] = p[i];
}
}
return c;
}