可通过Object.freeze、Object.defineProperty、私有字段#和配置对象处理四种方式实现实例属性防意外修改:前者浅冻结顶层属性,后者提供语言级隔离,中间两种精准控制关键字段,最后避免暴露可变钩子。

构造函数本身无法在实例化时自动锁定所有实例属性,但可以通过组合手段让新创建的实例具备“防意外修改”能力——核心不是阻止赋值动作,而是让修改无效或立即暴露问题。
用 Object.freeze 冻结实例对象
最直接的方式是在构造函数末尾调用 Object.freeze(this),使当前实例变成不可扩展、不可配置、不可写的状态:
- 新增属性(
this.newProp = 1)静默失败(非严格模式)或报错(严格模式) - 修改已有属性值(
this.name = 'x')同样无效 - 删除属性(
delete this.name)也失败
注意:这仅冻结顶层属性,若属性值是对象,其内部仍可修改(浅冻结)。如需深冻结,需额外递归处理,但通常不推荐对每个实例都深冻,性能和可维护性代价高。
用 Object.defineProperty 精确控制关键属性
对特别重要的字段(如 ID、创建时间),不依赖默认赋值,而用 Object.defineProperty 显式定义:
立即学习“Java免费学习笔记(深入)”;
-
writable: false→ 阻止重新赋值 -
configurable: false→ 防止被删除或重定义 -
enumerable: false→ 不出现在for...in或Object.keys()中,降低误操作风险
示例:Object.defineProperty(this, 'id', { value: Date.now(), writable: false, configurable: false, enumerable: false });
用私有字段(#)天然隔离敏感数据
ES2022+ 支持类私有字段,语法简单且语义明确:
-
#internalId = Date.now();—— 外部无法访问或修改,连Reflect.ownKeys()都看不到 - 配合 getter 提供只读接口,逻辑可控(比如校验后才返回)
- 无需手动冻结,也不依赖运行时防护,是语言级保障
这是目前最推荐的做法,尤其适用于核心业务字段。
避免在构造函数中暴露可变钩子
如果设计了供外部传入的配置对象(如 options),不要直接挂到 this 上并任由后续修改。应:
- 深拷贝或结构化克隆(
structuredClone)后再使用 - 或仅提取必要字段,忽略其余内容,不保留原始引用
- 对配置字段也按需用
defineProperty锁定,而非裸露赋值
否则,外部持有原配置对象的引用,仍可能间接影响实例行为。


















