扩展运算符{...obj}仅浅拷贝,基本类型和函数时安全;含嵌套对象或数组时会共享引用,需用structuredClone、JSON序列化或显式解构+深拷贝防护。

扩展运算符({...obj})是复制组件属性对象最常用、也最直观的方式,但它本身只做浅拷贝——对嵌套对象或数组不递归处理。要“安全”使用,关键不是避免它,而是清楚它的边界,并在必要时补足防护。
什么时候用扩展运算符复制属性是安全的
当组件接收的 props 全是基本类型(字符串、数字、布尔值、null、undefined)或函数(函数本身是引用,但通常不修改其内部状态),且没有嵌套对象、数组、日期、正则等复杂值时,{...props} 是完全安全的。
- 例如:
{...{ name: "Alice", active: true, onClick: handleClick }}—— 修改name或调用onClick不会影响原始对象 - React 中常见写法如
<input>就依赖这个前提
哪些情况会出问题
一旦 props 包含嵌套结构,扩展运算符就无法隔离变更:
将后端 Java 接口文档转换为前端 TypeScript 类型定义(interface / type)。输入:飞书文档、Spring Boot 接口文档、Swagger/OpenAPI、手写 API 文档等。输出:包含请求参数与响应结果的 .md 文件,含完整 JSDoc 注释。触发关键词:Java 转 TS、接口转换、生成前端类型、接口文档转 TypeScript、DTO/VO/BO 转 interface、后端文档转前端类型、生成接口类型、类型定义生成。
-
{ user: { id: 1, profile: { avatar: "url" } } }→ 复制后copy.user.profile === original.user.profile仍为true - 后续若执行
copy.user.profile.avatar = "new-url",原始对象也会被改掉 - 数组同理:
{ list: [1, 2, 3] }复制后,copy.list.push(4)会同步影响原数组
如何让扩展运算符更安全
不替换它,而是加一层轻量控制:
-
只解构明确需要的字段:避免全量展开,改用显式提取
{ name, age, ...rest },再按需处理rest中的敏感字段 -
对已知嵌套项单独深拷贝:比如确定
props.config是深层对象,可写成{ ...props, config: JSON.parse(JSON.stringify(props.config)) }(仅限无函数、无循环引用的简单配置) -
结合工具函数兜底:对高风险 props(如表单初始值、编辑态数据),提前用
structuredClone(现代浏览器支持)或lodash.cloneDeep预处理,再展开:{ ...structuredClone(props) }
替代方案参考(非必须,看场景)
如果项目中大量存在深层嵌套 props,且频繁修改,可考虑:
- 用
Object.assign({}, props)—— 行为与扩展运算符一致,但语法上更易插入中间逻辑(如过滤 key) - 封装一个带白名单的复制函数,例如:
safeSpread(props, ["id", "name", "settings"]) // settings 自动深拷贝 - 在组件内部用
useState(structuredClone(props))初始化本地状态,彻底切断引用关系
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










