JavaScript对象初始化器自引用相关报错原因及初始化流程问询
JavaScript对象初始化规则与示例报错原因解析
先明确对象字面量的初始化执行流程
JS引擎处理对象字面量(也就是你用{}写的对象初始化语法)的时候,严格遵循以下顺序:
- 先在堆内存中创建一个空的JS对象,生成对应的内存地址
- 按你写的键值对顺序,同步计算每一个属性的属性值,计算完成后立刻挂载到空对象上
- 所有属性挂载完成后,才会把对象的内存地址赋值给等号左侧的变量
另外要注意:用const/let声明的变量存在「暂时性死区」,在变量完成赋值之前,任何访问该变量的操作都会直接抛出错误。
三个示例的运行结果解释
1. 第一段代码报Maximum call stack size exceeded的原因
对象字面量本身不会创建新的this上下文,所以属性值里的this继承自外层作用域:
- 如果这段代码运行在全局作用域,
this就是全局对象(浏览器端为window,Node.js端为globalThis) - 全局对象本身会持有你声明的
obj变量的引用,形成obj → self → 全局对象 → obj的循环引用
部分运行环境的控制台在打印这个带循环引用的对象时,会因为无限递归遍历触发栈溢出错误。
2. 第二段代码报Cannot access uninitialized variable的原因
按照初始化流程,你在计算self的属性值时,整个对象还没完成所有属性的挂载,自然也还没把内存地址赋值给左侧的obj变量。这时候obj还处在暂时性死区,直接访问就会触发未初始化变量的报错。
3. 带方法初始化器的代码可以正常运行的原因
方法初始化器(a() {}、b() {}这种写法)本质是把函数作为属性值挂载到对象上,函数内部的代码不会在对象初始化阶段执行,只有当函数被调用的时候才会执行:
- 等你调用
obj.a()的时候,对象早就完成了初始化,obj变量已经被赋值为完成构建的对象,自然可以正常访问obj.name - 方法内的
this是调用时动态绑定的,调用obj.b()时this会自动指向调用方法的obj对象,所以也可以正常拿到this.name
核心规则总结
你提到的差异本质是代码执行时机不同:
- 普通属性的属性值是初始化阶段同步计算的,这时候对象还没构建完成、左侧变量还没赋值,直接引用未初始化的变量自然会报错
- 方法内部的代码是惰性执行的,调用时对象已经完成初始化,所以可以正常引用对象和对应的
this
内容的提问来源于stack exchange,提问作者tmarwen
相关产品推荐
相关产品推荐

