浅拷贝不改变属性访问限制,仅复制可枚举自有属性的值或引用;原对象的只读、冻结等状态不会自动继承,嵌套对象的共享才是访问异常的主因。

浅拷贝本身不施加任何访问限制,它只是复制方式的选择——复制后对象的属性是否可读、可写、是否被冻结,完全取决于原对象本身的属性描述符(如 writable、enumerable、configurable),而非拷贝动作本身。
浅拷贝不会改变属性的可访问性
Object.assign() 和展开运算符 {...obj} 都只复制“可枚举的自有属性”,且仅按值或引用复制属性内容,不会重新定义属性特性。
- 如果原对象某属性是
Object.defineProperty(obj, 'x', { writable: false })设置的只读属性,浅拷贝后该属性在新对象中仍是只读的(因为复制的是属性值,不是描述符) - 如果原对象属性被
Object.freeze()或Object.seal()处理过,浅拷贝得到的是一个普通新对象,不受原对象冻结状态影响——但注意:若该属性本身是引用类型(比如一个 frozen 对象),其内部状态仍被共享 - Symbol 键、不可枚举属性(如
__proto__或通过Object.defineProperty设置enumerable: false的属性)默认不会被浅拷贝捕获
常见误判场景:看似“访问受限”,实为引用共享
开发者常误以为“无法修改某个属性”是浅拷贝导致的限制,实际往往是嵌套对象被共用,而那个嵌套对象本身已被设为只读或冻结。
- 例:
const a = { inner: Object.freeze({ x: 1 }) }; const b = { ...a }; b.inner.x = 2;→ 报错,不是因为b有访问限制,而是b.inner === a.inner,且inner被冻结 - 再例:
const c = { d: { e: 1 } }; Object.defineProperty(c, 'd', { writable: false }); const d = Object.assign({}, c); d.d.e = 99;→ 成功,因为d.d是可写的(浅拷贝绕过了原c.d的 descriptor,只复制了引用)
如何真正控制拷贝后属性的访问权限
若需在拷贝后统一管控属性行为,必须显式干预,不能依赖浅拷贝自动继承或转换描述符:
- 用
Object.getOwnPropertyDescriptors()+Object.defineProperties()手动复制并调整描述符 - 对拷贝结果调用
Object.freeze()/Object.seal()施加新约束 - 使用
Proxy封装拷贝对象,拦截get/set实现动态访问控制
与深拷贝的关键对比点
深拷贝(如 structuredClone())同样不复制属性描述符——它只递归复制数据值。目前所有主流深拷贝方案(包括 Lodash cloneDeep、structuredClone、JSON 方案)均丢失 getter/setter、不可枚举性、configurable 等元信息。
- 唯一保留完整 descriptor 的方式是手动遍历
Object.getOwnPropertyDescriptors()并重建,无论深浅 - 也就是说:访问限制从来不是“拷贝类型”决定的,而是“你是否主动重建了属性定义”决定的
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











