深拷贝不提供只读保护,需组合冻结或proxy实现深层只读;推荐先structuredclone再递归object.freeze,或用proxy构建只读视图,class封装可兼顾类型安全与精细控制。

深拷贝本身不提供只读保护,它只是创建独立副本;要实现“深层只读”,需在深拷贝完成后,对副本对象施加只读约束。两者是正交问题:深拷贝解决**数据隔离**,只读机制解决**访问控制**。关键在于组合使用——先确保结构完全独立,再逐层冻结或代理拦截。
先深拷贝,再冻结:Object.freeze + 递归遍历
这是最直接、兼容性好、无运行时开销的方案,适合配置类静态数据。
- 用递归函数或
structuredClone()(现代浏览器)完成深拷贝,避免引用共享 - 对拷贝结果执行递归
Object.freeze():冻结当前对象,并对其所有嵌套对象属性(值为 object/array)也调用 freeze - 注意:
Object.freeze()不影响原始对象,只作用于副本;冻结后无法增删改属性,且configurable: false防止重新定义 - 示例逻辑:不处理循环引用时需加标记避免死循环
一步到位:Proxy 深层只读代理(含深拷贝语义)
用 Proxy 构建一个“只读视图”,内部自动触发深拷贝逻辑,适合需要动态拦截、日志或条件判断的场景。
- 代理对象在
get时,若属性值为对象/数组,返回其只读代理(递归 wrap),而非原始值 -
set、defineProperty、deleteProperty等全部拒绝并抛错 - 天然支持 Date、RegExp、Map、Set 等类型(只要你在 handler 中显式处理构造逻辑)
- 缺点:每次属性访问都有代理开销,不适合高频读取场景(如动画帧内)
Class 封装 + 私有字段 + 深拷贝初始化
从设计源头控制可变性,适合领域模型类,兼顾类型安全与只读语义。
- 构造时接收原始数据,立即用
structuredClone()或递归深拷贝存入私有字段#data - 所有 getter 返回对应属性的只读代理(如对对象属性再 wrap 一层 Proxy)或冻结副本
- 不暴露任何 setter,外部无法修改内部状态
- 优势:IDE 可推导类型,错误发生在编译/开发阶段;冻结或代理仅发生在必要路径上,更精细
谨慎使用 JSON 方案的只读增强
仅当确定输入是纯 JSON-safe 数据(无 function、undefined、Symbol、Date、RegExp、循环引用)时,可快速组合:
const readOnly = Object.freeze(JSON.parse(JSON.stringify(src)))- 该方式快但脆弱:Date 变字符串、RegExp 丢失、undefined 消失,且只冻结顶层——嵌套对象仍可写
- 若坚持用,必须配合递归 freeze,且需提前过滤/转换非 JSON 类型(例如把 Date 转成 ISO 字符串再存)











