为何ESM实例创建速度慢于CJS?Node.js实测对比
为什么CommonJS创建实例的速度远快于ES模块?
我编写了两段逻辑几乎一致的代码,分别采用ES模块(.mjs)和CommonJS(.js)格式,测试大量创建实例的性能差异,结果发现CommonJS的速度显著快于ES模块。
测试代码
1. a.mjs (ESM)
function Worker(health) { this._health = health ?? 10; } function JuniorEngineer(health, intelligence) { this._super(health); this._intelligence = intelligence ?? 1; this._isBornGenius = intelligence > 10; } Worker.prototype.work = function () { this._health--; }; Worker.prototype.getHealth = function () { return this._health; }; JuniorEngineer.prototype = Object.create(Worker.prototype, {}); JuniorEngineer.prototype._super = function (health) { return Worker.call(this, health); }; JuniorEngineer.prototype.getIntelligence = function () { return this._intelligence; }; JuniorEngineer.prototype.work = function () { Worker.prototype.work.call(this); this._intelligence++; }; JuniorEngineer.prototype.isBornGenius = function () { return this._isBornGenius; }; function main() { var startTime = performance.now(); console.time(); const amount = 10000000; for (var i = 0; i < amount; i++) { const junior = new JuniorEngineer(10, Math.floor(Math.random() * 20)); junior._isBornGenius; } var endTime = performance.now(); console.timeEnd(); } main(); export { Worker, JuniorEngineer };
2. a.js (CJS)
function Worker(health) { this._health = health ?? 10; } function JuniorEngineer(health, intelligence) { this._super(health); this._intelligence = intelligence ?? 1; this._isBornGenius = intelligence > 10; } Worker.prototype.work = function () { this._health--; }; Worker.prototype.getHealth = function () { return this._health; }; JuniorEngineer.prototype = Object.create(Worker.prototype, {}); JuniorEngineer.prototype._super = function (health) { return Worker.call(this, health); }; JuniorEngineer.prototype.getIntelligence = function () { return this._intelligence; }; JuniorEngineer.prototype.work = function () { Worker.prototype.work.call(this); this._intelligence++; }; JuniorEngineer.prototype.isBornGenius = function () { return this._isBornGenius; }; function main() { var startTime = performance.now(); console.time(); const amount = 10000000; for (var i = 0; i < amount; i++) { new JuniorEngineer(10, Math.floor(Math.random() * 20)).isBornGenius(); } var endTime = performance.now(); console.timeEnd(); } main(); module.exports = { Worker, JuniorEngineer };
测试结果
- ESM:
145.3415ms - CJS:
59.676541ms
性能差异的核心原因
1. 严格模式的默认启用
ES模块默认强制开启严格模式,而CommonJS默认不启用(除非手动声明'use strict')。严格模式下V8引擎的部分优化会受到限制:
- 严格模式禁止
this指向全局对象,构造函数执行时的this绑定逻辑更复杂,增加了引擎的处理开销。 - 严格模式下对变量、属性的访问检查更严格,会打断V8的内联优化和隐藏类(Hidden Class)优化路径。
2. V8引擎的优化优先级与成熟度
Node.js对CommonJS的优化迭代时间更长,V8引擎对CJS模块中的函数、构造函数的优化支持更完善:
- CJS模块的函数处于函数作用域,V8更容易将其标记为可优化的函数类型,比如进行内联优化、隐藏类的稳定化处理。
- ES模块的词法作用域特性,导致V8在处理模块内的构造函数时,需要额外处理作用域链,增加了运行时开销。
3. 模块加载与执行的底层差异
虽然测试核心是实例创建而非模块加载,但ESM的延迟解析(lazy parsing)机制会影响首次执行时的函数编译:
- ESM采用静态解析,会先进行模块依赖分析再编译执行,而CJS是加载时立即编译执行,构造函数的编译时机更早,引擎有更多时间进行预优化。
4. 测试代码的细微差异(次要因素)
ESM测试中直接访问实例属性junior._isBornGenius,而CJS中调用isBornGenius()方法。不过这个差异对性能的影响远小于前面的核心因素,统一测试逻辑后(比如两者都调用方法或都直接访问属性),耗时差距依然存在,只是略有缩小。
验证建议
- 在CJS文件顶部添加
'use strict',会发现其性能接近ESM的耗时,验证严格模式的影响。 - 调整测试循环中的操作,统一属性访问方式,排除测试逻辑的干扰。
内容的提问来源于stack exchange,提问作者CHOI GIHOAWN
相关产品推荐
相关产品推荐

