深入剖析JavaScript面向对象编程:构造函数的继承

作者:半夏时间:2026-10-30 10:17:26浏览:419

本文深入解析JavaScript中构造函数继承的多种实现方式,包括构造函数绑定、prototype模式、直接继承prototype、空对象中介、封装函数及拷贝继承。详细对比各方法的优缺点,重点分析prototype修改对constructor的影响及解决方案,帮助开发者掌握JS面向对象编程的核心技巧。

JavaScript构造函数继承概述

二、JavaScript 面向对象编程:构造函数的继承

本节重点阐述如何生成同时继承多个对象的实例。

例如,假设存在一个表示“动物”的构造函数,


function Animal(){ 
this.species = "动物"; 
}

以及另一个表示“猫”的构造函数,


function Cat(name,color){ 
this.name = name; 
this.color = color; 
} 

如何实现“猫”对“动物”的继承?

构造函数绑定与prototype模式

1. 构造函数绑定

最简便的方式是利用 call 或 apply 方法,将父类构造函数绑定至子类,即在子类构造函数内部添加如下代码:


 function Cat(name,color){ 
Animal.apply(this, arguments); 
this.name = name; 
this.color = color; 
} 
var cat1 = new Cat("大毛","黄色"); 
alert(cat1.species); // 动物 

2. prototype 模式

更为常见的做法是借助 prototype 属性。

若“猫”的 prototype 对象指向一个 Animal 实例,则所有“猫”的实例均可继承 Animal 的属性。


Cat.prototype = new Animal(); 
Cat.prototype.constructor = Cat; 
var cat1 = new Cat("大毛","黄色"); 
alert(cat1.species); // 动物 

代码首行将 Cat 的 prototype 对象指向一个 Animal 实例。

1. Cat.prototype = new Animal();

此举完全替换了 prototype 对象的原有值,但第二行代码的作用何在?

1. Cat.prototype.constructor = Cat;

每个 prototype 对象均具备一个 constructor 属性,指向其对应的构造函数。因此,Cat.prototype 的 constructor 属性应指向 Cat。

前一步骤已移除原 prototype 对象的值,导致新 prototype 对象缺失 constructor 属性。若不手动补全,后续“继承链”将出现故障。此即第二行代码的含义。

此为关键原则,编程时务必遵循。下文均依此原则执行,即若替换了 prototype 对象,

1. o.prototype = {};

则下一步必须为新的 prototype 对象添加 constructor 属性,并将其指回原构造函数。

1. o.prototype.constructor = o;

直接继承prototype及其缺陷

3. 直接继承 prototype

鉴于 Animal 对象中不变属性可直接写入 Animal.prototype,Cat() 亦可跳过调用 Animal(),直接继承 Animal.prototype。

首先改写 Animal 对象:

1. function Animal(){ } 2. Animal.prototype.species = "动物";

随后,将 Cat 的 prototype 对象指向 Animal 的 prototype 对象,从而完成继承。


Cat.prototype = Animal.prototype; 
CatCat.prototype.constructor = Cat; 
var cat1 = new Cat("大毛","黄色"); 
alert(cat1.species); // 动物 

相较于前一种方法,此举优势在于效率更高(无需执行及创建 Animal 实例),且节省内存。然而,Cat.prototype 与 Animal.prototype 现指向同一对象,对 Cat.prototype 的任何修改均会反映至 Animal.prototype。

因此,上述代码存在缺陷,请留意第二行:

1. Cat.prototype.constructor = Cat;

该语句实际上也修改了 Animal.prototype 对象的 constructor 属性!

1. alert(Animal.prototype.constructor); // Cat

空对象中介与封装函数

4. 利用空对象作为中介

鉴于“直接继承 prototype”存在上述缺陷,可采用空对象作为中介。


var F = function(){}; 
F.prototype = Animal.prototype; 
Cat.prototype = new F(); 
Cat.prototype.constructor = Cat; 

F 为空对象,几乎不占用内存。此时,修改 Cat 的 prototype 对象不会影响 Animal 的 prototype 对象。

1. alert(Animal.prototype.constructor); // Animal

5. prototype 模式的封装函数

将上述方法封装为函数,以方便调用。


function extend(Child, Parent) { 
 
var F = function(){}; 
F.prototype = Parent.prototype; 
Child.prototype = new F(); 
Child.prototype.constructor = Child; 
Child.uber = Parent.prototype; 
} 

使用时,方法如下:


extend(Cat,Animal); 
var cat1 = new Cat("大毛","黄色"); 
alert(cat1.species); // 动物 

此 extend 函数即为 YUI 库实现继承的方式。

此外说明一点,函数体最后一行:

1. Child.uber = Parent.prototype;

意为为子对象设置 uber 属性,该属性直接指向父对象的 prototype 属性。这相当于在子对象上开辟一条通道,以便直接调用父对象方法。该行代码仅用于确保继承的完备性,属备用性质。

拷贝继承实现

6. 拷贝继承

前述方法采用 prototype 对象实现继承。亦可换用思路,纯粹通过“拷贝”方式实现继承。简言之,若将父对象的所有属性与方法拷贝至子对象,是否也能实现继承?

首先,将 Animal 的所有不变属性置于其 prototype 对象上。

1. function Animal(){} 2. 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; 
}

该函数旨在将父对象 prototype 对象中的属性逐一拷贝至 Child 对象的 prototype 对象。

使用时,写法如下:


extend2(Cat, Animal); 
var cat1 = new Cat("大毛","黄色"); 
alert(cat1.species); // 动物 

以上关于深入剖析 JavaScript 面向对象编程的内容即为小编分享的全部,希望能提供参考,也希望大家多多支持本站。

作者最新文章