如何通过 Object.create(Object.prototype) 构建具有标准功能且属性可控的独立对象?

2026-04-30 20:441阅读0评论SEO基础
  • 内容介绍
  • 相关推荐

本文共计801个文字,预计阅读时间需要4分钟。

如何通过 Object.create(Object.prototype) 构建具有标准功能且属性可控的独立对象?

使用 `Object.create(Object.prototype)` 可以创建一个干净的普通对象:

为什么不用 {}new Object()

看似等价,但有细微却关键的区别:

  • {}new Object() 创建的对象,其内部原型确实是 Object.prototype,但它们依赖于当前运行时中 Object.prototype 的状态 —— 如果该原型被篡改(如有人执行了 Object.prototype.foo = ...),这些字面量对象也会继承这个污染属性;
  • Object.create(Object.prototype) 明确指定原型,语义更清晰,且在严格模式或某些沙箱环境中更可控;
  • 更重要的是,它不触发构造函数逻辑(无 Object 调用开销,也避免潜在副作用)。

如何让属性“受控”?

“受控”指对属性的添加、修改、删除行为进行显式约束。核心是配合 Object.definePropertyObject.defineProperties,结合描述符(descriptor)精确控制每个属性的行为:

  • writable: false 防止值被重赋;
  • enumerable: false 让属性不出现在 for...inObject.keys() 中;
  • configurable: false 锁定属性本身(无法删、无法改描述符);
  • 若需只读访问,可定义 get 函数,省略 set(或设为抛错)。

例如:

const controlled = Object.create(Object.prototype); Object.defineProperties(controlled, { id: { value: 123, writable: false, enumerable: true, configurable: false }, name: { get() { return this._name; }, set(val) { if (typeof val === 'string' && val.trim()) { this._name = val.trim(); } else { throw new TypeError('name must be a non-empty string'); } }, enumerable: true } });

注意原型链与属性查找的关系

即使对象自身属性受控,仍可通过原型链访问 Object.prototype 上的方法(如 controlled.toString())。但若想进一步隔离——比如禁用某些方法(如禁止 hasOwnProperty 被调用),就不能直接用 Object.prototype 作原型,而应使用一个“精简原型”,例如:

const safeProto = {}; safeProto.toString = Object.prototype.toString; safeProto.valueOf = Object.prototype.valueOf; // 不挂载 hasOwnProperty / isPrototypeOf / propertyIsEnumerable 等 const ultraControlled = Object.create(safeProto);

这样既保留基础类型识别能力,又规避了部分可能被滥用的原型方法。

实际使用中的推荐组合

多数场景下,兼顾标准性与可控性的做法是:

  • Object.create(Object.prototype) 创建空对象;
  • Object.defineProperties 一次性定义所有初始属性,明确各描述符;
  • 后续新增属性时,始终通过 Object.defineProperty 添加,并保持一致策略;
  • 必要时封装为工厂函数,复用受控逻辑。

例如:

function createControlledUser(data) { const obj = Object.create(Object.prototype); Object.defineProperties(obj, { uid: { value: data.uid, writable: false, configurable: false }, role: { get() { return this._role || 'user'; }, set(v) { this._role = ['admin', 'user', 'guest'].includes(v) ? v : 'user'; } } }); return obj; }

本文共计801个文字,预计阅读时间需要4分钟。

如何通过 Object.create(Object.prototype) 构建具有标准功能且属性可控的独立对象?

使用 `Object.create(Object.prototype)` 可以创建一个干净的普通对象:

为什么不用 {}new Object()

看似等价,但有细微却关键的区别:

  • {}new Object() 创建的对象,其内部原型确实是 Object.prototype,但它们依赖于当前运行时中 Object.prototype 的状态 —— 如果该原型被篡改(如有人执行了 Object.prototype.foo = ...),这些字面量对象也会继承这个污染属性;
  • Object.create(Object.prototype) 明确指定原型,语义更清晰,且在严格模式或某些沙箱环境中更可控;
  • 更重要的是,它不触发构造函数逻辑(无 Object 调用开销,也避免潜在副作用)。

如何让属性“受控”?

“受控”指对属性的添加、修改、删除行为进行显式约束。核心是配合 Object.definePropertyObject.defineProperties,结合描述符(descriptor)精确控制每个属性的行为:

  • writable: false 防止值被重赋;
  • enumerable: false 让属性不出现在 for...inObject.keys() 中;
  • configurable: false 锁定属性本身(无法删、无法改描述符);
  • 若需只读访问,可定义 get 函数,省略 set(或设为抛错)。

例如:

const controlled = Object.create(Object.prototype); Object.defineProperties(controlled, { id: { value: 123, writable: false, enumerable: true, configurable: false }, name: { get() { return this._name; }, set(val) { if (typeof val === 'string' && val.trim()) { this._name = val.trim(); } else { throw new TypeError('name must be a non-empty string'); } }, enumerable: true } });

注意原型链与属性查找的关系

即使对象自身属性受控,仍可通过原型链访问 Object.prototype 上的方法(如 controlled.toString())。但若想进一步隔离——比如禁用某些方法(如禁止 hasOwnProperty 被调用),就不能直接用 Object.prototype 作原型,而应使用一个“精简原型”,例如:

const safeProto = {}; safeProto.toString = Object.prototype.toString; safeProto.valueOf = Object.prototype.valueOf; // 不挂载 hasOwnProperty / isPrototypeOf / propertyIsEnumerable 等 const ultraControlled = Object.create(safeProto);

这样既保留基础类型识别能力,又规避了部分可能被滥用的原型方法。

实际使用中的推荐组合

多数场景下,兼顾标准性与可控性的做法是:

  • Object.create(Object.prototype) 创建空对象;
  • Object.defineProperties 一次性定义所有初始属性,明确各描述符;
  • 后续新增属性时,始终通过 Object.defineProperty 添加,并保持一致策略;
  • 必要时封装为工厂函数,复用受控逻辑。

例如:

function createControlledUser(data) { const obj = Object.create(Object.prototype); Object.defineProperties(obj, { uid: { value: data.uid, writable: false, configurable: false }, role: { get() { return this._role || 'user'; }, set(v) { this._role = ['admin', 'user', 'guest'].includes(v) ? v : 'user'; } } }); return obj; }