proxy 的 set 陷阱可在属性赋值前拦截并强校验数据类型,需用 reflect.set 安全赋值、集中 validator 函数、避免隐式转换,支持联动更新与异步校验。

利用 Proxy 的 set 陷阱 可以在属性赋值的瞬间完成数据类型校验,并结合响应式更新机制实现实时同步——关键不是“事后检查”,而是在值写入前拦截、验证、转换或拒绝。
用 set 陷阱做类型强校验
set 陷阱接收三个参数:target(原对象)、property(属性名)、value(待设的值)。你可在此处对 value 做类型断言,不满足则抛错或静默处理。
- 检查基础类型:如
typeof value === 'string'或Number.isInteger(value) - 校验复合结构:用
Array.isArray(value)、value instanceof Date或自定义类构造器判断 - 拒绝 null/undefined:显式写
if (value == null) throw new TypeError(...),避免隐式转换干扰 - 注意:返回
Reflect.set(target, property, value)是推荐做法,它能正确处理原型链和 setter 属性
配合 Reflect.set 实现安全赋值与同步触发
校验通过后,不要直接写 target[property] = value,因为这会绕过原型上的 setter,也丢失对 getter/setter 的兼容性。应统一使用 Reflect.set()。
- 它返回布尔值,便于做条件分支;失败时自动抛出错误,行为更可控
- 若需同步通知(比如触发 UI 更新),可在
Reflect.set()成功后调用自定义回调,如notifyChange(property, value) - 多个字段联动时,可在 set 中检测 property 是否为关键字段,再批量更新关联属性(例如改了
email,自动重算domain)
避免常见陷阱:类型宽松 ≠ 类型安全
很多开发者用 value + '' 或 Number(value) 强转后再存,这看似“容错”,实则掩盖问题、破坏数据语义。
- 字符串转数字时,
Number('') === 0、Number('abc') === NaN,但 NaN 不是有效数值 - 建议用严格解析:如
parseInt(value, 10)配合 isNaN 判断,或用Number.isFinite() - 对日期,优先用
new Date(value).getTime()并检查是否为有效时间戳,而非只看是否为 Date 实例 - 所有校验逻辑应集中在一个 validator 函数中,避免重复写 if-else,也方便单元测试
扩展:校验 + 同步 + 日志的一体化封装
可将校验规则、变更通知、操作日志打包进一个工厂函数,生成带行为约束的代理对象:
- 每个字段绑定独立 schema(如
{ type: 'number', min: 1, required: true }) - set 里执行 schema 校验,失败则 throw,成功则触发
onChange和onLog - 支持异步校验(如邮箱唯一性检查),此时 set 返回 Promise,需搭配 async/await 使用场景
- 最终代理对象既保证输入合法,又天然支持响应式更新链路,无需额外监听或 watch










