ECMAScript对象字面量演进本质是从自由创建走向精细管控,核心是逐步暴露并约束[[Configurable]]、[[Enumerable]]、[[Writable]]、[[Value]]及[[Get]]/[[Set]]等内部特性。

ECMAScript规范中对象字面量的演进与属性控制,本质是一条从“自由创建”走向“精细管控”的路径。核心线索不在语法糖本身,而在于属性背后那套隐式但可干预的内部特性([[Configurable]]、[[Enumerable]]、[[Writable]]、[[Value]] 和 [[Get]]/[[Set]]),以及规范如何逐步暴露并约束它们。
对象字面量:从便捷语法到特性的默认载体
对象字面量 { name: "Alice" } 看似简单,实则是 ECMAScript 最早确立的、最常用的对象创建方式。它直接生成一个拥有数据属性的对象,且这些属性在创建时就自动获得一组默认内部特性:
-
[[Configurable]]、[[Enumerable]]、[[Writable]] 全为
true -
[[Value]] 为赋的值(如
"Alice")
这意味着你可以随时删、遍历、改——这种“宽松性”是 ES3/ES5 早期的默认行为。直到 ES5 引入 Object.defineProperty(),才首次允许开发者显式覆盖这些默认值,让字面量创建的对象也能被“加固”或“封装”。
属性控制的分水岭:ES5 的 Object.defineProperty() 机制
ES5 是属性控制能力的真正起点。它没有改变字面量本身的语法,而是提供了一套统一的底层接口来操作任意对象(包括字面量创建的)的属性特性:
- 调用
Object.defineProperty(obj, "prop", { writable: false })后,“prop”变成只读,非严格模式下赋值静默失败,严格模式下抛TypeError - 一旦
configurable: false,该属性就“冻结”了:不能再删、不能再改其他特性(除writable外),也不能转成访问器属性 - 访问器属性(
get/set)必须通过此 API 定义,无法在字面量中直接写出(ES6 的简写get foo() {}是语法糖,底层仍走同一机制)
这个 API 的出现,标志着规范从“描述对象怎么用”,转向“定义对象该怎么被定义”。它把原本隐藏在引擎内部的属性行为,变成了可编程、可验证的契约。
ES6+ 的演进:语法糖补全,语义更严谨
ES6 并未推翻 ES5 的属性模型,而是在其上叠加更自然的表达方式,同时强化语义一致性:
- 对象字面量支持简写方法
{ method() {} }和简写访问器{ get prop() {}, set prop(v) {} },但它们最终仍被编译为带[[Get]]/[[Set]]特性的访问器属性 - 计算属性名
{ [key]: value }让属性名动态化,但不改变属性本身的特性规则;它的值仍是标准的数据或访问器属性 - 解构赋值、展开运算符(
{...obj})等新语法,都遵循原始对象各属性的 [[Enumerable]] 和 [[Configurable]] 状态——不可枚举的属性不会被展开,不可配置的属性在复制后依然保持不可配置
换句话说,ES6 的所有新对象语法,都是对 ES5 属性模型的“友好封装”,而非替代。底层控制逻辑始终由 Object.defineProperty() 及其衍生方法(如 Object.defineProperties()、Object.freeze())承载。
现代实践中的关键认知
理解这条线索,能帮你避开常见误区:
- 字面量创建的对象不是“特殊对象”,它和
new Object()创建的对象在属性特性上完全等价 -
const obj = { x: 1 }中的const只锁引用,不锁属性;要真正保护属性,必须靠defineProperty或Object.freeze() - 框架响应式系统(如 Vue 2 的
Object.defineProperty代理、Vue 3 的Proxy)本质上是对这套属性控制机制的深度利用与扩展
不复杂但容易忽略:对象的行为,从来不由你“怎么写”决定,而由你“怎么配”它的内部特性决定。


















