Object.defineProperties 要求第二个参数必须是属性描述符对象,每个键对应含 configurable、enumerable、writable、value/get/set 的完整描述符;传普通对象会因缺少必要字段而抛 TypeError。

Object.defineProperties 为什么不能直接传普通对象字面量
它要求第二个参数必须是属性描述符对象,也就是每个键对应的值都得是 configurable、enumerable、writable、value、get、set 的组合。如果你直接传一个普通对象(比如 { name: 'foo', age: 18 }),JavaScript 会把它当作「描述符」来解析,而 name: 'foo' 这种写法既没 value 也没 get,会被当成无效描述符,抛出 TypeError: Property description must be an object。
正确传入的描述符对象长什么样
每个属性名对应一个完整描述符对象,必须显式声明你要设置的特性。常见组合有:
- 数据属性:至少含
value或writable,推荐同时设writable和configurable - 访问器属性:必须含
get或set(或两者),不能同时有value或writable - 不设某字段就等于用默认值:
configurable和enumerable默认为false,writable默认为false(对数据属性)
示例:
const obj = {};
Object.defineProperties(obj, {
name: {
value: 'Alice',
writable: false,
configurable: true,
enumerable: true
},
age: {
get() { return this._age; },
set(v) { this._age = v; },
configurable: true,
enumerable: false
}
});
和 Object.defineProperty 对比:什么时候该用哪个
单个属性用 Object.defineProperty 更简洁;多个属性且需要统一控制(比如批量设为不可枚举、禁止删除)时,Object.defineProperties 更合适——它本质就是循环调用 defineProperty,但避免了手动写 for 循环,也保证原子性(全部成功或全部失败,不会出现中间态)。
- 性能上无本质差异,V8 等引擎已对两者做类似优化
- 注意:如果描述符对象里某个属性名是
__proto__,不同引擎行为不一致,建议避开 - IE9+ 支持,但 IE9 不支持访问器属性(
get/set)在非严格模式下的定义
容易被忽略的陷阱:可配置性与后续修改权限
configurable: false 是最易踩坑的点。一旦设为 false,之后就不能再用 Object.defineProperty 修改该属性的任何描述符(包括从数据属性改访问器属性),也不能 delete 它。而且,即使 writable: true,只要 configurable: false,你就无法把 writable 改回 false(反之可以)。
- 想保留后期调整余地?初始设
configurable: true,上线前再批量设为false - 用
Object.getOwnPropertyDescriptors(obj)可安全读取当前描述符,避免手写漏字段 - 冻结对象后(
Object.freeze),所有属性自动变成configurable: false, writable: false,此时再调用defineProperties会静默失败(非严格模式)或报错(严格模式)
批量定义属性这事本身不难,难的是想清楚每个字段背后的控制权归属——尤其是 configurable,设错一次,后面基本没法补救。

















