JavaScript中5种继承方式详解与对比

作者:函数茶馆

时间:2026-10-24

浏览:149

本文详细解析JavaScript中五种常见的继承模式,包括原型链、构造函数、组合式、寄生式及寄生组合式继承。通过原理说明与代码示例,对比各模式的优缺点,重点探讨如何解决属性共享、传参限制及重复调用构造函数等问题,帮助开发者选择最优继承方案。

文章目录

1.原型链继承

核心原理:将子类的原型对象(prototype)指向父类的一个实例,从而实现原型共享,即执行 Son.prototype = new Father()。

在此先简要回顾原型机制。

在JavaScript中,每个对象都包含一个 __proto__ 属性,该属性指向其对应的原型对象。同时,每个函数都拥有 prototype 属性,它指向由该函数作为构造函数创建出来的实例的原型。原型对象上存在一个 constructor 属性,指向创建该对象的构造函数,且该属性默认是不可枚举的。


var obj = {};
obj.__proto__ === Object.prototype; //true
console.log(Object.prototype.constructor) // ƒ Object

当访问对象的属性或方法时,若自身未找到,JavaScript引擎会沿原型链向上查找;若原型中仍未找到,则继续向上层原型查找。例如,调用 obj.toString 时,若自身未定义该方法,引擎会在 Object.prototype 中找到并执行。


var obj = {};
obj.toString();
obj.__proto__.toString(); //obj.__proto__和Object.prototype指向的是一个对象,自然就能访问Object.prototype上的toString方法啦 

需注意:原型链的终点是 null,且使用 bind 方法返回的函数不再拥有 prototype 属性。


var obj = {};
function fn(){};
fn.bind(obj).prototype; // undefined
Object.prototype.__proto__; // null

原型链继承


function Father(age){
 this.age = age;
 this.color = ['red','pink']
}
Father.prototype.sayHello = function(){
 console.log('hello')
} 
function Son(sex){
 this.sex = sex
}
console.log(Son.prototype.constructor) // ƒ Son
Son.prototype = new Father(15) // 原型链继承关键
var son = new Son('男')
son.color.push('black')
var son2 = new Son('女')
son.sayHello() // hello
son.sayHello === son2.sayHello //true
console.log(son2.color) // ['red','pink','black']
console.log(Son.prototype.constructor) // ƒ Father

通过原型链实现继承时,原型上引用类型的值会被所有实例共享。此外,子类的 constructor 指向会发生改变,且在创建子类实例时无法向父类构造函数传递参数。可以通过手动将子类 constructor 属性重新指向其构造函数来修正。


//写法一
Son.prototype.constructor = Son // 这种写法有点缺点,它会让constructor属性变的可以枚举。

//写法二
Object.defineProperty(Son.prototype,'constructor',{
 enumerable:false, // 设置不可枚举
 value:Son
})

2.构造函数继承

原理:在子类构造函数内部,通过 apply 或 call 方法调用父类构造函数,从而继承属性或方法。


function Father(name){
 this.color = ['red']
 this.sayHello = function(){
  console.log('hello')
 }
}
Father.prototype.sayName = function(){
 console.log('zs')
}
function Son(num,name){
 Father.call(this,name) //实现继承的关键代码
 this.num = num
}
var son = new Son(10,'zs')
var son2 = new Son(15,'ls')
son.color.push('pink')
console.log(son2.color) // ['red']
son.sayName() //报错 son.sayName is not a function
console.log(son.sayHello === son2.sayHello) //false

可以看出,通过构造函数实现继承解决了原型链继承中无法向父类传参以及引用类型值共享的问题。但这种继承方式无法访问父类构造函数原型上的方法和属性,且定义在父类构造函数中的方法也无法实现复用。

3.组合式继承

组合式继承,有时也称为伪经典继承,是一种结合原型链继承和构造函数继承的继承模式。其实现思路是:通过原型链实现对原型属性和方法的继承,同时通过借用构造函数实现对实例属性的继承。


function Father(name){
 this.color = ['red']
}
Father.prototype.sayName = function(){
 console.log('zs')
}
function Son(num,name){
 Father.call(this,name) //继承实例属性
 this.num = num
}
Son.prototype = new Father() //继承原型上属性
Son.prototype.constructor = Son
var son = new Son(10,'zs')
var son2 = new Son(15,'ls')
son.color.push('pink')
console.log(son.color,son2.color) //['red','pink'] ['red']
son.sayName() // zs

组合式继承避免了原型链继承和构造函数继承各自的缺点,融合了它们的优点,成为JavaScript中常用的一种继承模式。

4.寄生式继承

寄生式继承与工厂模式类似,通常用于继承对象。即创建一个封装继承逻辑的函数,在函数内部复制一份该对象,对复制的对象进行处理,最后返回处理后的对象。


function createAnother(obj){
  var clone = Object.create(obj)
  clone.name = 'zs'
  clone.sayHello = function(){
   console.log('hello')
  }
  return clone
}
var obj = {age:15}
var newObj = createAnother(obj) // 15
console.log(newObj.name) // zs
newObj.sayHello() // hello

5.寄生组合式继承

前文提到,组合式继承是JavaScript中最常用的继承模式,但它存在一个不足:会调用两次父类构造函数。第一次是在将子类原型指向父类实例时,第二次是在子类构造函数中调用时。


function Father(name){
  this.name = name
 }
 function Son(num,name){
  Father.call(this,name) // 第二次调用
 }
 Son.prototype = new Father('ls') // 第一次调用
 var son = new Son(10,'zs')
 console.log(son)

在第一次调用时,Son.prototype 会继承 name 属性;第二次调用时,实例对象会继承 name。当我们获取实例对象的 name 属性时,由于实例对象上已有该属性,因此不会去原型中查找,这导致实例对象上的 name 属性屏蔽了原型上的 name 属性,使得原型上的该属性变得多余。

1.原型链继承

为解决此问题,寄生组合式继承应运而生。其主要思路是创建一个函数来完成原型链继承和 constructor 指向问题,然后通过构造函数继承属性。


 // 复制一个父类的原型指向,将子类的原型指向复制的父类原型,达到不用调用父类构造函数就能继承其原型上的方法的效果。
 function inherit(Sup,Sub){
  var prototype = Object.create(Sup.prototype)
  Sub.prototype = prototype
  prototype.constructor = Sub
 }
 function Father(name){
  this.name = name
 }
 function Son(name){
 Father.call(this,name)
 }
 inherit(Father,Son)
 var son = new Son('zs')
 console.log(son)

1.原型链继承

以上就是JavaScript中常用的几种继承方式。

到此这篇关于JavaScript中的几种继承方法的文章就介绍到这了,更多相关JavaScript继承方法内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章