JavaScript类的get访问器是否为每个实例单独分配内存?
结论
你观察到的堆快照结果是正确的:写在class类体内的get访问器只会分配一次,由所有实例共享,不会增加单个实例的内存占用。
原理说明
ES6 class的语法本质是原型继承的语法糖,你在类体中定义的所有普通方法、get/set访问器,最终都会被挂载到构造函数的prototype属性指向的原型对象上:
// 你写的class语法,等价于以下原型操作 function Test() {} Object.defineProperties(Test.prototype, { a: { get() { return 1; } }, b: { get() { return 2; } }, // 其余访问器同理 })
所有通过new Test()创建的实例,自身只会存储自定义的实例属性,以及一个隐式的内部指针[[Prototype]](也就是常说的__proto__)指向Test.prototype,访问实例上的属性时如果实例本身没有,就会沿着原型链向上查找原型上的访问器,因此不需要每个实例都单独存储一份get访问器。
适用条件与例外场景
这个结论是ECMAScript规范明确定义的行为,所有主流浏览器引擎(Chrome的V8、Firefox的SpiderMonkey、Safari的JavaScriptCore)都严格遵循,不存在兼容性问题,你做浏览器游戏可以放心依赖。只有两种例外情况会导致访问器被重复分配:
- 你在构造函数内部为单个实例单独定义访问器,例如:
class Test { constructor() { // 错误写法:每个实例都会单独创建一份get a函数,占用额外内存 Object.defineProperty(this, 'a', { get() { return 1; } }) } } - 你使用了实例字段语法定义箭头函数形式的访问器(实际不是访问器,是实例属性存箭头函数),也会导致每个实例单独存储:
class Test { // 错误写法:这是实例属性,不是原型上的get访问器,每个实例单独存 getA = () => 1; }
开发建议
对于内存要求高的浏览器游戏,你完全可以把所有通用的访问器、方法都定义在class类体中,不管加多少个都不会增加实例的内存开销,原型上的属性只会初始化一次,对整体内存占用的影响可以忽略。
内容的提问来源于stack exchange,提问作者Ryan Peschel
相关产品推荐
相关产品推荐

