JavaScript实现继承的三种核心方式详解

作者:纸飞机时间:2026-10-30 04:17:21浏览:650

本文深入解析JavaScript中实现继承的三种主流方法。首先介绍利用Object.create构建类式继承的手动原型链设置;其次分析Node.js中util.inherits工具函数的实现原理及其对Object.setPrototypeOf的依赖;最后详解ES6 Class语法糖中extends关键字与super函数的使用规范,对比各方案在代码简洁性与执行效率上的差异。

基于Object.create的类式继承

利用Object.create方法可以实现类式继承模式。

参考官方文档提供的示例代码。


//Shape - superclass
function Shape() {
 this.x = 0;
 this.y = 0;
}

Shape.prototype.move = function(x, y) {
  this.x += x;
  this.y += y;
  console.info("Shape moved.");
};

// Rectangle - subclass
function Rectangle() {
 Shape.call(this); //call super constructor.
}

Rectangle.prototype = Object.create(Shape.prototype);

var rect = new Rectangle();

rect instanceof Rectangle //true.
rect instanceof Shape //true.

rect.move(1, 1); //Outputs, "Shape moved."

在此模式下,Rectangle原型的constructor属性默认指向父类构造函数。若需让实例识别自身构造函数,需手动修正该属性,具体做法如下。

Rectangle.prototype.constructor = Rectangle;

util.inherits工具函数解析

另一种方式是利用Node.js utilities工具包提供的util.inherits函数。

其标准语法格式如下。

util.inherits(constructor, superConstructor) 具体应用示例如下。


const util = require('util');
const EventEmitter = require('events');

function MyStream() {
  EventEmitter.call(this);
}

util.inherits(MyStream, EventEmitter);

MyStream.prototype.write = function(data) {
  this.emit('data', data);
}

var stream = new MyStream();

console.log(stream instanceof EventEmitter); // true
console.log(MyStream.super_ === EventEmitter); // true

stream.on('data', (data) => {
 console.log(`Received data: "${data}"`);
})
stream.write('It works!'); // Received data: "It works!"

该实现过程相对直观,其底层源码实际上调用了ES6引入的新特性,我们进一步分析其内部机制。


exports.inherits = function(ctor, superCtor) {

 if (ctor === undefined || ctor === null)
  throw new TypeError('The constructor to "inherits" must not be ' +
            'null or undefined');

 if (superCtor === undefined || superCtor === null)
  throw new TypeError('The super constructor to "inherits" must not ' +
            'be null or undefined');

 if (superCtor.prototype === undefined)
  throw new TypeError('The super constructor to "inherits" must ' +
            'have a prototype');

 ctor.super_ = superCtor;
 Object.setPrototypeOf(ctor.prototype, superCtor.prototype);
};

其中Object.setPrototypeOf即为ES6新特性,将一个指定的对象的原型设置为另一个对象或者null

其语法定义如下。

Object.setPrototypeOf(obj, prototype) 其中obj参数代表需要修改原型的对象。 prototype参数代表obj新的原型对象,该值可以是任意对象或null。

若将原型设置为null,则示例代码如下。

Object.setPrototypeOf({}, null); 从命名即可看出,该函数专注于处理prototype属性。那么其底层实现原理是什么?这需要借助非标准的__proto__属性来完成。


Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
 obj.__proto__ = proto;
 return obj; 
}

即直接将proto赋值给obj.__proto__即可完成原型链的构建。

ES6 Class与extends关键字

第三种方式是直接使用ES6的extends关键字。

熟悉Java语法的开发者对该关键字应不陌生,Java中的继承机制主要依赖此关键字。ES6新增的class关键字本质上是语法糖,其底层实现依然基于函数机制。

在以下示例中,首先定义了一个名为Polygon的类,随后定义了一个继承自Polygon的Square类。注意构造函数中使用了super()调用,super()仅能在构造函数内部使用,且必须在this关键字可用之前被调用。


class Polygon {
 constructor(height, width) {
  this.name = 'Polygon';
  this.height = height;
  this.width = width;
 }
}

class Square extends Polygon {
 constructor(length) {
  super(length, length);
  this.name = 'Square';
 }
}

使用关键字后,无需再繁琐地手动设置原型链,该关键字已封装好相关逻辑,使用更加快捷方便。

作者最新文章