JavaScript - 继承的几种实现方式
April 12, 2025
6 min read
#原型链继承
基于对象的 原型链
来实现继承是JavaScript的核心机制。JavaScript中的继承是通过 原型链
实现的:当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么它就会去它的 原型对象
里找这个属性,这个原型对象又会有自己的原型,于是就这样一直找下去,也就是 原型链
的概念。原型链的尽头一般来说都是 Object.prototype
。
缺点:原型链继承JavaScript
function Weather() {this.condition = 'Sunny'}Weather.prototype.getCondition = function () {return this.condition}function Forecast() {}Forecast.prototype = new Weather() // 继承自WeatherForecast.prototype.constructor = Forecastconst forecast = new Forecast()console.log(forecast.getCondition()) // "Sunny"
- 包含
引用类型
的原型属性会被所有实例属性共享,容易造成属性的修改混乱 - 在创建子类型的实例时,不能向父类构造函数中传递参数
基于以上问题,在实践中很少会单独使用原型链。
#构造函数继承
构造函数继承的思想主要是在子类构造函数中调用父类构造函数,使用 call
或 apply
方法来改变函数执行时的上下文。
优点:构造函数继承JavaScript
function Weather(condition) {this.condition = condition}function Forecast(condition, temperature) {Weather.call(this, condition) // 继承自Weatherthis.temperature = temperature}const forecast = new Forecast('Cloudy', 22)console.log(forecast.condition) // "Cloudy"console.log(forecast.temperature) // 22
- 可以在子类型构造函数中向父类构造函数添加参数
- 解决了原型链继承中
引用类型
属性共享的问题
- 只能继承父类的
实例属性
和方法,不能继承原型链
上的属性和方法 - 每次创建子类实例都会执行父类的构造函数,性能开销较大
基于以上问题,构造函数继承的技术也是很少单独使用的。
#组合式继承
组合继承(也称为 伪经典继承
)结合了原型链继承和构造函数继承的优点。
优点:组合继承JavaScript
function Weather(condition) {this.condition = condition}Weather.prototype.getCondition = function () {return this.condition}function Forecast(condition, temperature) {Weather.call(this, condition) // 继承实例属性this.temperature = temperature}Forecast.prototype = new Weather() // 继承原型属性Forecast.prototype.constructor = Forecastconst forecast = new Forecast('Rainy', 18)console.log(forecast.getCondition()) // "Rainy"console.log(forecast.temperature) // 18
- 组合继承结合了原型链继承和构造函数继承的优点,既可以继承父类的
实例属性
(包括引用类型
的属性),也可以继承父类的原型方法
- 通过构造函数调用,子类的实例属性不会共享父类的
引用类型
属性,从而避免了原型链继承中可能引发的属性共享问题
- 调用了两次父类的构造函数,导致基类的
原型对象
中增添了不必要的父类的实例对象中的所有属性
#寄生式组合继承
寄生式继承
的思路是创建一个仅用于封装继承过程的函数,该函数在内部以某种方式增强对象,最后返回这个对象。这种方式是目前最理想的JavaScript继承范式。
优点:寄生式组合继承JavaScript
function Weather(condition) {this.condition = condition}Weather.prototype.getCondition = function () {return this.condition}function Forecast(condition, temperature) {Weather.call(this, condition) // 继承实例属性this.temperature = temperature}// 使用Object.create方法创建一个新对象,使用现有的对象来作为新创建对象的原型Forecast.prototype = Object.create(Weather.prototype)Forecast.prototype.constructor = Forecastconst forecast = new Forecast('Snowy', -5)console.log(forecast.getCondition()) // "Snowy"console.log(forecast.temperature) // -5
- 解决了组合继承的冗余调用问题,性能更优
- 继承了父类的
实例属性
和原型属性
- 被认为是引用类型最理想的继承范式
- 使用该继承方式,在为对象添加函数的时候,没有办法做到函数的复用,实现稍微复杂一些
#Class 继承
ES6 引入了 class
关键字,提供了更直观的语法糖来实现继承。这种语法更加清晰易懂,但本质上仍然是基于原型的继承。
优点:ES6 Class继承JavaScript
class Weather {constructor(condition) {this.condition = condition}getCondition() {return this.condition}}class Forecast extends Weather {constructor(condition, temperature) {super(condition) // 继承实例属性this.temperature = temperature}}const forecast = new Forecast('Windy', 15)console.log(forecast.getCondition()) // "Windy"console.log(forecast.temperature) // 15
- 语法更加简洁和直观
- 支持继承
静态方法
- 更好的结构化代码,符合
面向对象编程
的习惯
- 只是
语法糖
,本质上仍然基于原型链继承