为何类声明创建实例的效率远高于直接对象声明?
这个问题问得非常实在!我们可以从JavaScript引擎的底层优化逻辑,以及两种写法的本质差异来拆解背后的原因,先回顾下你的测试代码和结果:
测试代码
console.time("class decls"); class Dog { a; constructor() { this.a = 1; } bark() { this.a + this.a; } } for (let i = 0; i < 1_000_000; ++i) { let dog = new Dog(); dog.bark(); } console.timeEnd("class decls"); console.time("object decls"); for (let i = 0; i < 1_000_000; ++i) { let dog = { a: undefined, bark() { this.a + this.a; } } { // inlining the constructor dog.a = 1; } dog.bark(); } console.timeEnd("object decls");
测试结果(Bun环境)
[8.46ms] class decls [27.98ms] object decls
接下来具体分析效率差异的核心原因:
1. 方法的复用逻辑完全不同
类的bark方法是挂载在Dog.prototype上的,所有Dog实例都会共享这同一个方法对象。当你循环new Dog()时,每个实例只是在原型链上引用这个已存在的方法,不需要重复创建函数。
而你的直接对象声明写法里,每次循环都会重新创建一个全新的bark函数——虽然代码逻辑一样,但每次都是一个独立的函数对象,这会带来额外的内存分配、函数初始化开销,引擎还要处理这些临时函数的垃圾回收,自然会慢很多。
2. 引擎对类的专属优化支持
现代JavaScript引擎(比如V8、Bun使用的JavaScriptCore衍生引擎)对类有专门的优化策略:
- 类的结构是静态可预测的:类定义一旦确定,实例的属性、原型链结构就固定了,引擎可以提前做类型推断、编译优化,甚至把构造逻辑内联成更高效的机器码,并且缓存这些优化结果。
- 类的构造函数会被引擎标记为“稳定类型构造器”,每次
new Dog()时,引擎可以直接复用之前编译好的逻辑,不需要重复分析。
而直接对象字面量的写法,虽然你在循环里写的结构看起来一样,但引擎从语法层面无法100%保证每次创建的对象结构完全一致(理论上你可以在循环里动态修改对象结构),所以很难像类那样做激进的稳定类型优化。
3. 构造逻辑的优化差异
类的constructor是一个被引擎特殊处理的函数,它的逻辑会被提前编译优化,甚至和new操作符的逻辑合并成更高效的执行路径。
而你在对象字面量里模拟的“构造逻辑”(单独的dog.a = 1赋值),只是循环里的普通赋值语句,引擎无法把它和对象创建逻辑做整体的优化,只能按普通代码逐行执行,效率自然不如类构造的批量优化。
简单总结:类的写法天生更符合JavaScript引擎的优化预期,它的静态结构、原型复用特性让引擎可以做大量提前优化;而直接对象字面量在循环里重复创建,会带来大量重复的函数创建和不可预测的结构,引擎无法做同样激进的优化,所以两者的效率差距会被百万次循环放大得很明显。
备注:内容来源于stack exchange,提问作者vs07

