本文解析 JavaScript 中类继承的核心机制,涵盖构造函数 prototype 与实例 __proto__ 的关系。通过分析 TypeScript 编译后的 __extends 函数及 extendStatics 逻辑,展示如何利用 Object.setPrototypeOf 或 Object.create 实现原型链挂载,从而理解子类访问父类属性和方法的底层实现。
JavaScript 类继承的核心概念
首先需要明确两个核心概念:
- 构造函数的
prototype属性指向实例的原型,该属性值是一个对象,允许被重新赋值。例如,为简化代码,有时会将prototype属性直接赋值为一个对象字面量; - 每个对象内部都有一个
__proto__属性,指向其自身的原型。尽管该属性非标准,但主流浏览器均已实现,MDN 文档中提到的[[Prototype]]即对应__proto__。例如,实例的__proto__属性指向了构造函数的prototype;
__extends 函数源码解析
以下代码片段源自 @better-scroll/core,展示了类继承的具体实现方式。
// extendStatics函数的作用是实现类静态方法的继承,构造函数原型继承,最终结果是d可以访问b的属性和方法。
var extendStatics = function (d, b) {
// Object.setPrototypeOf() 方法设置一个指定的对象的原型(即,内部 [[Prototype]] 属性)到另一个对象或 null。是Object.prototype.__proto__ 的替代
extendStatics = Object.setPrototypeOf ||
(
// 这里的逻辑是:如果当前环境支持 __proto__关键字, 就用__proto__修改原型的指向
{
__proto__: []
}
// instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
instanceof Array && function (d, b) {
d.__proto__ = b; // 把d的原型指向b
}
) ||
/* 如果以上两者修改原型指向的方法都不支持那么手动将b上的属性添加到d上 */
function (d, b) {
for (var p in b) {
// 如果p是对象b自身的属性,那么将属性p添加到对象d上
if (Object.prototype.hasOwnProperty.call(b, p)) {
d[p] = b[p];
}
}
};
return extendStatics(d, b);
};
function __extends(d, b) {
extendStatics(d, b);
// 这里 __ 函数的作用是: 1. 保证d的构造函数指向正确;2.通过 __ 的实例实现继承;
function __() {
this.constructor = d;
}
__.prototype = b.prototype;
//Object.create() 方法创建一个新对象,使用现有的对象来提供新创建的对象的 __proto__
// 如果b不是null, d.prototype = new __(); d.prototype.__proto__ = b.prototype;
d.prototype = b === null ? Object.create(b) : new __();
}
针对 __extends 函数中的相关逻辑:
function __() {
this.constructor = d;
}
__.prototype = b.prototype;
d.prototype = new __();
基于 Object.create 的简化实现
实际上,这段逻辑也可以通过 Object.create() 来实现。使用 Object.create() 改写后的 __extends 代码如下:
function __extends(d, b) {
extendStatics(d, b);
d.prototype = Object.create(b.prototype);
d.prototype.constructor = d;
}至此,关于 JavaScript 类继承实现方法的介绍完毕。如需了解更多 JS 类继承相关内容,请查阅本站往期文章或继续浏览后续文章,感谢大家的支持!