JavaScript原型链继承的四种实现方式与优缺点解析

作者:小括号时间:2026-10-28 20:59:20浏览:594

本文深入分析JavaScript原型链继承的四种实现方式。从基础的原型赋值,到修正constructor属性,再到解决原型共享副作用的直接引用传递,最后介绍使用临时构造器隔离原型的最佳实践。通过对比各方案的代码结构与内存影响,帮助开发者理解继承机制并选择合适方案。

JavaScript原型链继承基础示例

本文通过具体实例深入解析JavaScript中原型链继承的应用方法,旨在为读者提供清晰的参考与学习思路。

以下是第一种基础实现方式的代码示例:

function Shape(){
this.name = 'shape';
this.toString = function(){
return this.name;
 }  
}  
  
function TwoDShape(){
this.name = '2D shape';
}  
function Triangle(side,height){
this.name = 'Triangle';
this.side = side;
this.height = height;
this.getArea = function(){
return this.side*this.height/2;
 };  
}  
  
/* inheritance */
TwoDShape.prototype = new Shape();
Triangle.prototype = new TwoDShape();
 

修正constructor属性的必要性

在完全重写对象的prototype属性后,往往会影响constructor属性的指向。因此,建立好继承关系后,手动重置constructor是一个良好的编码习惯。具体操作如下:

修正constructor的代码如下:

TwoDShape.prototype.constructor = TwoDShape;
Triangle.prototype.constructor = Triangle;

原型属性移至原型的改写方案

接下来是对上述代码的改写版本,主要优化点在于将实例属性移至原型上:

改写后的代码如下:

function Shape(){}
  
Shape.prototype.name = 'shape';
Shape.prototype.toString = function(){
return this.name;
}  
  
function TwoDShape(){
  
TwoDShape.prototype = new Shape();
TwoDShape.prototype.constructor = TwoDShape;
  
TwoDShape.prototype.name = '2d shape';
  
function Triangle(side,height){
this.side = side;
this.height = height;
}  
  
Triangle.prototype = new TwoDShape;
Triangle.prototype.constructor = Triangle;
  
Triangle.prototype.name = 'Triangle';
Triangle.prototype.getArea = function(){
return this.side*this.height/2;
}

直接引用传递的副作用分析

第三种改写方式采用了直接引用传递而非实例化,虽然提升了效率,但引入了一个副作用:由于父子对象共享同一个原型对象,修改子对象的原型属性会直接改变父对象的状态。

直接引用传递的代码如下:

function Shape(){}
  
Shape.prototype.name = 'shape';
Shape.prototype.toString = function(){
return this.name;
}  
  
function TwoDShape(){
  
TwoDShape.prototype = Shape.prototype;
TwoDShape.prototype.constructor = TwoDShape;
  
TwoDShape.prototype.name = '2d shape';
  
function Triangle(side,height){
this.side = side;
this.height = height;
}  
  
Triangle.prototype = TwoDShape.prototype;
Triangle.prototype.constructor = Triangle;
  
Triangle.prototype.name = 'Triangle';
Triangle.prototype.getArea = function(){
return this.side*this.height/2;
}

尽管这种方式提高了执行效率,但由于是引用传递而非值传递,父对象中的name属性会受到子对象修改的影响。子对象和父对象指向的是同一个原型对象,因此一旦子对象修改原型,父对象也会随之改变。

使用临时构造器隔离原型

为了解决上述副作用,我们采用临时构造器进行改写,这是目前较为推荐的方案:

使用临时构造器的代码如下:

function Shape(){}
Shape.prototype.name = 'shape';
Shape.prototype.toString = function(){
return this.name;
}  
function TwoDShape(){
var F = function(){
F.prototype = Shape.prototype;
TwoDShape.prototype = new F();
TwoDShape.prototype.constructor = TwoDShape;
TwoDShape.prototype.name = '2d shape';
function Triangle(side,height){
this.side = side;
this.height = height;
}  
F.prototype = TwoDShape.prototype;
Triangle.prototype = new F();
Triangle.prototype.constructor = Triangle;
Triangle.prototype.name = 'Triangle';
Triangle.prototype.getArea = function(){
return this.side*this.height/2;
}

虽然这种方式提高了效率,但需注意其副作用。由于涉及引用传递逻辑,父对象中的name值仍可能受到间接影响。

子对象和父对象最终指向的是通过临时构造器隔离后的不同对象。然而,若实现不当导致原型直接共享,子对象修改原型时父对象仍会被改变。

希望本文所述的JavaScript程序设计内容能对您的学习与实践有所帮助。

作者最新文章