6种JavaScript继承方式及优缺点(小结)

作者:拾光时间:2026-10-24 03:32:42浏览:561

本文系统梳理了JavaScript中六种常见的继承实现方式,包括原型链继承、构造函数继承、组合继承、原型式继承、寄生继承以及寄生组合继承。文章通过代码示例详细解析了每种方式的工作原理、优缺点及适用场景,重点指出寄生组合继承为目前最推荐的方案,旨在帮助开发者深入理解JS面向对象编程的核心机制。

JavaScript继承基础概念

在深入探讨JavaScript的继承机制之前,建议先掌握构造函数、原型对象、实例化对象以及原型链等基础概念,这将有助于更好地理解后续的继承方式。

原型链继承

第一种实现方式:原型链继承

该方法基于原型链的特性来实现继承关系。


function Parent(){
  this.name = 'web前端';
  this.type = ['JS','HTML','CSS'];
}
Parent.prototype.Say=function(){
  console.log(this.name);
}
function Son(){};
Son.prototype = new Parent();
son1 = new Son();
son1.Say();

具体实现逻辑解释如下:

① 定义一个名为Parent的构造函数(父构造函数),其中包含name和type两个属性。 ② 在Parent的原型对象上定义Say方法,此时Parent拥有2个属性和1个方法。 ③ 定义一个名为Son的构造函数(子构造函数)。 ④ 将Son的原型对象设置为Parent的实例,即Son.prototype = new Parent(),从而实现Son对Parent的继承,此时Son也具备了Parent的2个属性和1个方法。 ⑤ 实例化Son构造函数,将结果赋值给变量son1,son1作为实例化对象,同样拥有2个属性和1个方法。 ⑥ 调用son1的Say方法,输出结果为"web前端"。

优点:能够实现基本的继承功能。

缺点: ① 由于Son.prototype继承的是Parent的实例,所有Son的实例化对象共享原型对象的属性和方法,导致数据无法独立。代码示例如下:


son1 = new Son();
son2 = new Son();
son1.type.push('VUE');
console.log(son1.type);//['JS','HTML','CSS','VUE']
console.log(son2.type);//['JS','HTML','CSS','VUE']

运行结果显示,son1和son2的type数组均为['JS','HTML','CSS','VUE'],说明修改其中一个实例会影响另一个。 ② 在Son构造函数实例化时,无法向父级构造函数传递参数。

构造函数继承

第二种实现方式:构造函数继承

通过调用构造函数的call方法来实现继承,具体代码示例如下:


function Parent(){
  this.name = 'web前端';
  this.type = ['JS','HTML','CSS'];
}
function Son(){
  Parent.call(this);
}
son1 = new Son();
son1.type.push('VUE');
console.log(son1.type);//['JS','HTML','CSS','VUE']
son2 = new Son();
console.log(son2.type);//['JS','HTML','CSS']

具体实现逻辑解释如下: ① 定义父级构造函数Parent,包含name和type两个属性。 ② 定义子级构造函数Son,在函数内部通过call方法调用父级构造函数Parent,从而完成继承。 ③ 分别创建Son的两个实例son1和son2。对son1的type属性追加元素,而son2未进行追加,两者结果不同,表明实现了实例间的独立性。

优点: ① 实现了实例化对象的独立性,各实例拥有独立的属性。 ② 支持在实例化时向父级构造函数传递参数。


function Parent(name){
  this.name = name;
}
function Son(name){
  Parent.call(this,name);
}
son1 = new Son('JS');
console.log(son1);//JS
son2 = new Son('HTML');
console.log(son2);//HTML

缺点: ① 方法定义在构造函数内部,每次实例化对象时都会重新创建方法,导致无法实现函数复用。 ② call方法仅调用了父级构造函数的属性和方法,无法访问父级构造函数原型对象上定义的方法。

组合继承

第三种实现方式:组合继承

结合原型链继承和构造函数继承的优势进行使用,具体代码示例如下:


function Parent(name){
  this.name = name;
  this.type = ['JS','HTML','CSS'];
}
Parent.prototype.Say=function(){
  console.log(this.name);
}
function Son(name){
  Parent.call(this,name);
}
Son.prototype = new Parent();
son1 = new Son('张三');
son2 = new Son('李四');
son1.type.push('VUE');
son2.type.push('PHP');
console.log(son1.type);//['JS','HTML','CSS','VUE']
console.log(son2.type);//['JS','HTML','CSS','PHP']
son1.Say();//张三
son2.Say();//李四

具体实现逻辑解释如下: ① 定义Parent构造函数,包含name和type两个属性。 ② 在Parent的原型对象上定义Say方法,此时Parent有2个属性和1个方法。 ③ 定义子级构造函数Son,在函数内部通过call方法调用父级构造函数Parent,实现属性继承。 ④ 将Son.prototype设置为new Parent(),使Son继承Parent的方法,此时Son拥有2个属性和1个方法。 ⑤ 分别创建Son的两个实例son1和son2,传入不同参数,给type属性追加不同元素,并调用原型对象上的Say方法。

优点: ① 利用原型链继承,实现了原型对象方法的复用和继承。 ② 利用构造函数继承,实现了属性的独立继承,并支持参数传递。 组合继承基本满足了JavaScript继承的需求,是较为常用的方式。

缺点: 无论何种情况,都会调用两次父级构造函数:一次是在创建子级原型时(Son.prototype = new Parent()),另一次是在子级构造函数内部(Parent.call(this))。

原型式继承

第四种实现方式:原型式继承

创建一个函数,将传入的对象作为新对象的原型对象。


function fun(obj) {
  function Son(){};
  Son.prototype = obj;
  return new Son();
}    
var parent = {
  name:'张三'
}
var son1 = fun(parent);
var son2 = fun(parent);
console.log(son1.name);//张三
console.log(son2.name);//张三

具体实现逻辑解释如下: ① 定义一个函数fun,内部定义构造函数Son。 ② 将Son的原型对象设置为传入的参数(一个对象),完成继承关系。 ③ 实例化Son并返回,即返回的是一个实例化对象。 优缺点:与原型链继承类似,存在共享引用类型属性的问题。

寄生继承

第五种实现方式:寄生继承

在原型式继承的基础上,在函数内部对对象进行增强和丰富。


function fun(obj) {
  function Son() { };
  Son.prototype = obj;
  return new Son();
}
function JiSheng(obj) {
  var clone = fun(obj);
  clone.Say = function () {
    console.log('我是新增的方法');
  }
  return clone;
}
var parent = {
  name: '张三'
}
var parent1 = JiSheng(parent);
var parent2 = JiSheng(parent);
console.log(parent2.Say==parent1.Say);// false

具体实现逻辑解释如下: ① 在原型式继承的基础上,封装一个JiSheng函数。 ② 获取fun函数返回的对象,为其新增Say方法,最后返回该对象。 ③ 调用JiSheng函数两次,分别赋值给变量parent1和parent2。 ④ 对比parent1和parent2的Say方法,结果为false,说明实现了方法的独立性。 优缺点:与构造函数继承类似,每次调用函数都会创建新方法,无法实现函数复用,效率较低。

补充知识点:ES5引入了Object.create()方法,该方法封装了原型式继承。它接收两个参数:第一个是新对象的原型对象(可选),第二个是新对象新增的属性。因此,上述代码也可以改写为使用Object.create()的形式:


function JiSheng(obj) {
  var clone = Object.create(obj);
  clone.Say = function () {
    console.log('我是新增的方法');
  }
  return clone;
}
var parent = {
  name: '张三'
}
var parent1 = JiSheng(parent);
var parent2 = JiSheng(parent);
console.log(parent2.Say==parent1.Say);// false

寄生组合继承

第六种实现方式:寄生组合继承

结合组合继承和寄生继承的优势。组合继承的缺点在于会调用两次父级构造函数,一次在创建子级原型时,另一次在子级构造函数内部。优化思路是减少一次调用,利用寄生继承的特性,通过继承父级构造函数的原型来创建子级原型。


function JiSheng(son,parent) {
  var clone = Object.create(parent.prototype);//创建对象
  son.prototype = clone;   //指定对象
  clone.constructor = son;   //增强对象
}
function Parent(name){
  this.name = name;
  this.type = ['JS','HTML','CSS'];
}
Parent.prototype.Say=function(){
  console.log(this.name);
}
function Son(name){
  Parent.call(this,name);
}
JiSheng(Son,Parent);
son1 = new Son('张三');
son2 = new Son('李四');
son1.type.push('VUE');
son2.type.push('PHP');
console.log(son1.type);//['JS','HTML','CSS','VUE']
console.log(son2.type);//['JS','HTML','CSS','PHP']
son1.Say();//张三
son2.Say();//李四

具体实现逻辑解释如下: ① 封装一个JiSheng函数,接收两个参数:子级构造函数和父级构造函数。 ② 利用Object.create()将父级构造函数的原型克隆为副本clone。 ③ 将该副本作为子级构造函数的原型。 ④ 为副本添加constructor属性,因为在步骤③中修改原型导致副本失去了默认的constructor属性。

优缺点:结合了组合继承和寄生继承的优点,避免了多次调用父级构造函数的问题,是目前JavaScript继承中推荐使用的最佳方式。

以上即为本文的全部内容,希望对读者学习JavaScript继承机制有所帮助,欢迎继续支持本站。

作者最新文章