应从设计源头杜绝向 this 直接挂载 name、length 等系统保留属性,强制业务数据存入独立命名空间(如 $$data)、用 Object.defineProperty 控制属性特性、启用严格模式、结合 new.target 与白名单校验、优先使用 Object.create(null) 隔离数据,并通过 ESLint、TypeScript 类型标注及 AST 扫描在构建期拦截高危赋值。

避免在用 new 绑定动态扩展对象时覆盖系统保留属性,关键不是“绕开检查”,而是从设计源头切断误写路径——核心是**不给 this 赋值系统级属性的机会**。
明确禁止向 this 直接挂载保留名
JavaScript 中如 name、length、prototype、constructor、__proto__、toString 等,是函数或对象的固有属性。在构造函数中执行 this.name = xxx 会静默覆盖原生行为(尤其在非严格模式下)。
- 始终把业务数据存入独立命名空间,例如
this.$$data = { name: 'Alice' }或this.props = { ... } - 用
Object.defineProperty显式控制可枚举性与可写性,避免意外暴露:Object.defineProperty(this, 'id', { value: uuid(), writable: false, enumerable: false }) - 禁用裸属性赋值:在构造函数开头加
'use strict';,让this.name = ...直接报错而非静默覆盖
用 new.target + 命名白名单做运行时防护
new.target 本身不防覆盖,但它能帮你确认调用合法性,从而放心启用更严格的校验逻辑:
- 只在
new.target存在时才允许执行属性注入逻辑,排除裸调用污染全局的风险 - 配合白名单机制,在赋值前校验键名:
const SAFE_KEYS = new Set(['id', 'status', 'payload']);<br>if (!SAFE_KEYS.has(key)) throw new TypeError(`不允许动态设置保留属性: ${key}`); - 对用户传入的初始化参数做深度过滤,丢弃所有以
__、$开头或匹配^[a-z]+[A-Z](驼峰内置属性风格)的 key
优先使用 Object.create(null) 隔离数据容器
若只需存储动态字段,不要复用构造函数的 this 实例作为数据桶——它天然携带原型链和保留属性。
- 创建纯净数据对象:
this.data = Object.create(null); - 所有动态字段一律写入
this.data.xxx,彻底避开this.toString、this.valueOf等被覆盖风险 - 需要序列化时统一代理:
JSON.stringify(this.data),不依赖this自身的toJSON方法
构建期静态拦截保留属性赋值
靠运行时防御总有遗漏,应在编码阶段就卡住问题:
- ESLint 启用
no-restricted-properties规则,禁止对this写入name、length等关键词 - TypeScript 中为构造函数 this 类型显式标注,排除保留属性:
class User { constructor(name: string) { this as { id: string; $$meta: any }; this.id = genId(); } } - CI 流程中加入 AST 扫描脚本,检测
this\.[a-zA-Z]+ =模式并告警高危赋值

















