对象拷贝是 JavaScript 中常见的操作,分为浅拷贝和深拷贝。理解它们的区别对于正确处理数据至关重要。
赋值只是传递引用,不会创建新对象。
const obj1 = { a: 1, b: 2 }
const obj2 = obj1
obj2.a = 10
console.log(obj1.a) // 10(原对象被修改)
console.log(obj1 === obj2) // true(指向同一对象)内存示意:
栈内存 堆内存
┌──────────┐ ┌─────────────┐
│ obj1: 0x1 ├─────────→│ { a: 10, │
├──────────┤ │ b: 2 } │
│ obj2: 0x1 ├─────────→│ │
└──────────┘ └─────────────┘浅拷贝创建新对象,但嵌套对象仍然是引用。
const obj1 = { a: 1, b: { c: 2 } }
const obj2 = { ...obj1 }
obj2.a = 10
obj2.b.c = 20
console.log(obj1.a) // 1(第一层独立)
console.log(obj1.b.c) // 20(嵌套对象共享)
console.log(obj1 === obj2) // false
console.log(obj1.b === obj2.b) // true(嵌套对象相同)内存示意:
栈内存 堆内存
┌──────────┐ ┌─────────────┐
│ obj1: 0x1 ├─────────→│ { a: 1, │
├──────────┤ │ b: 0x3 } │──┐
│ obj2: 0x2 ├─────┐ └─────────────┘ │
└──────────┘ │ │
│ ┌─────────────┐ │
└───→│ { a: 10, │ │
│ b: 0x3 } │───┘
└─────────────┘
│
┌────▼────┐
│ { c: 20 }│ (共享)
└──────────┘深拷贝递归复制所有层级,创建完全独立的对象。
const obj1 = { a: 1, b: { c: 2 } }
const obj2 = JSON.parse(JSON.stringify(obj1))
obj2.b.c = 20
console.log(obj1.b.c) // 2(原对象不受影响)
console.log(obj1.b === obj2.b) // false(完全独立)内存示意:
栈内存 堆内存
┌──────────┐ ┌─────────────┐
│ obj1: 0x1 ├─────────→│ { a: 1, │
├──────────┤ │ b: 0x3 } │──┐
│ obj2: 0x2 ├─────┐ └─────────────┘ │
└──────────┘ │ │
│ ┌─────────────┐ │ ┌──────────┐
└───→│ { a: 1, │ └───→│ { c: 2 } │
│ b: 0x4 } │───────→└──────────┘
└─────────────┘ │
│ ┌──────────┐
└───→│ { c: 20 }│
└──────────┘浅拷贝只复制对象的第一层属性,嵌套对象仍然是引用。
Object.assign(target, ...sources) 将所有可枚举属性从一个或多个源对象复制到目标对象。
const obj = { a: 1, b: { c: 2 } }
const copy = Object.assign({}, obj)
copy.a = 10
copy.b.c = 20
console.log(obj.a) // 1
console.log(obj.b.c) // 20(嵌套对象被修改)
// 合并多个对象
const merged = Object.assign({}, { a: 1 }, { b: 2 }, { c: 3 })
console.log(merged) // { a: 1, b: 2, c: 3 }
// 注意:后面的属性会覆盖前面的
const override = Object.assign({}, { a: 1, b: 1 }, { b: 2, c: 3 })
console.log(override) // { a: 1, b: 2, c: 3 }ES6 扩展运算符 {...obj} 是最简洁的浅拷贝方式。
const obj = { a: 1, b: { c: 2 } }
const copy = { ...obj }
copy.a = 10
copy.b.c = 20
console.log(obj.a) // 1
console.log(obj.b.c) // 20(嵌套对象被修改)
// 合并对象(更优雅)
const merged = { ...{ a: 1 }, ...{ b: 2 }, ...{ c: 3 } }
console.log(merged) // { a: 1, b: 2, c: 3 }
// 添加/覆盖属性
const extended = { ...obj, d: 4, a: 100 }
console.log(extended) // { a: 100, b: { c: 20 }, d: 4 }slice() 方法返回数组的一部分的浅拷贝。
const arr = [1, 2, { a: 3 }]
const copy = arr.slice()
copy[0] = 10
copy[2].a = 30
console.log(arr[0]) // 1
console.log(arr[2].a) // 30(嵌套对象被修改)
// 常用于数组拷贝
const fullCopy = arr.slice() // 复制全部
const partialCopy = arr.slice(1, 3) // 复制索引 1-2concat() 方法用于合并数组,返回新数组。
const arr = [1, 2, { a: 3 }]
const copy = arr.concat()
copy[0] = 10
copy[2].a = 30
console.log(arr[0]) // 1
console.log(arr[2].a) // 30(嵌套对象被修改)
// 合并多个数组
const merged = [1, 2].concat([3, 4], [5, 6])
console.log(merged) // [1, 2, 3, 4, 5, 6]Array.from() 从可迭代或类数组对象创建新数组。
const arr = [1, 2, { a: 3 }]
const copy = Array.from(arr)
copy[0] = 10
copy[2].a = 30
console.log(arr[0]) // 1
console.log(arr[2].a) // 30(嵌套对象被修改)
// 转换类数组
const arrayLike = { 0: 'a', 1: 'b', length: 2 }
const arr2 = Array.from(arrayLike) // ['a', 'b']
// 使用 map 函数
const doubled = Array.from([1, 2, 3], x => x * 2)
console.log(doubled) // [2, 4, 6]使用 Object.create() 可以创建一个新对象,使用现有对象作为原型。
const obj = { a: 1, b: { c: 2 } }
const copy = Object.create(
Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj)
)
copy.a = 10
copy.b.c = 20
console.log(obj.a) // 1
console.log(obj.b.c) // 20(嵌套对象被修改)
// 特点:可以复制 getter/setter 和不可枚举属性
const original = {
get value() { return this._value },
set value(v) { this._value = v }
}
original._value = 10
const clone = Object.create(
Object.getPrototypeOf(original),
Object.getOwnPropertyDescriptors(original)
)
console.log(clone.value) // 10(getter 被保留)深拷贝递归复制所有层级的属性,创建完全独立的对象。
利用 JSON 序列化和反序列化实现深拷贝。
const obj = {
a: 1,
b: { c: 2 },
date: new Date(),
regex: /test/,
func: function () { return 'hello' },
undef: undefined,
symbol: Symbol('key')
}
const copy = JSON.parse(JSON.stringify(obj))
console.log(copy.a) // 1
console.log(copy.b.c) // 2
console.log(copy.date) // 字符串 "2024-01-01T00:00:00.000Z",不是 Date 对象
console.log(copy.regex) // {},正则表达式丢失
console.log(copy.func) // undefined,函数丢失
console.log(copy.undef) // undefined,但属性也被删除了
console.log(copy.symbol) // undefined,Symbol 丢失限制:
| 数据类型 | 处理结果 |
|---|---|
undefined | 被忽略(属性删除) |
Symbol | 被忽略(属性删除) |
Function | 被忽略(属性删除) |
Date | 转为字符串 |
RegExp | 转为空对象 {} |
Map/Set | 转为 {} |
Error | 转为 {} |
BigInt | 抛出错误 |
| 循环引用 | 抛出错误 |
| 不可枚举属性 | 被忽略 |
适用场景:
手动实现递归深拷贝,可以处理各种特殊情况。
function deepClone(obj, hash = new WeakMap()) {
// 处理 null 和非对象类型
if (obj === null || typeof obj !== 'object') {
return obj
}
// 处理循环引用
if (hash.has(obj)) {
return hash.get(obj)
}
// 处理特殊对象
if (obj instanceof Date) return new Date(obj.getTime())
if (obj instanceof RegExp) return new RegExp(obj.source, obj.flags)
const clone = obj instanceof Map ? new Map()
: obj instanceof Set ? new Set()
: Object.create(Object.getPrototypeOf(obj)) // 保留原型链
// 先缓存再递归,防止循环引用
hash.set(obj, clone)
if (obj instanceof Map) {
obj.forEach((value, key) => clone.set(deepClone(key, hash), deepClone(value, hash)))
} else if (obj instanceof Set) {
obj.forEach(value => clone.add(deepClone(value, hash)))
} else {
for (const key of Reflect.ownKeys(obj)) {
clone[key] = deepClone(obj[key], hash)
}
}
return clone
}
// 测试
const obj = {
a: 1,
b: { c: 2 },
date: new Date('2024-01-01'),
regex: /test/g,
func: function () { return 'hello' }
}
obj.self = obj
const copy = deepClone(obj)
console.log(copy.a) // 1
console.log(copy.b.c) // 2
console.log(copy.date instanceof Date) // true
console.log(copy.regex instanceof RegExp) // true
console.log(copy.func()) // 'hello'
console.log(copy.self === copy) // true浏览器原生提供的深拷贝 API,性能好且支持多种类型。
const obj = {
a: 1,
b: { c: 2 },
date: new Date(),
regex: /test/,
map: new Map([['key', 'value']]),
set: new Set([1, 2, 3]),
arrayBuffer: new ArrayBuffer(8),
blob: new Blob(['hello'])
}
const copy = structuredClone(obj)
console.log(copy.date instanceof Date) // true
console.log(copy.map instanceof Map) // true
console.log(copy.set instanceof Set) // true
console.log(copy.arrayBuffer instanceof ArrayBuffer) // true
console.log(copy.blob instanceof Blob) // true
// 支持循环引用
obj.self = obj
const copy2 = structuredClone(obj)
console.log(copy2.self === copy2) // true支持的数据类型:
| 类型 | 支持 | 说明 |
|---|---|---|
| 原始类型 | ✓ | string, number, boolean, null, undefined, BigInt |
| Date | ✓ | 完整支持 |
| RegExp | ✓ | 但 lastIndex 不保留 |
| Map/Set | ✓ | 完整支持 |
| ArrayBuffer | ✓ | 完整支持 |
| TypedArray | ✓ | Int8Array, Uint8Array 等 |
| Blob/File | ✓ | 完整支持 |
| Error | ✓ | 保留 message/name/stack 等内置字段 |
不支持的数据类型:
| 类型 | 处理结果 |
|---|---|
Function | 抛出 DataCloneError |
WeakMap / WeakSet | 抛出 DataCloneError |
DOM 节点(如 Element) | 抛出 DataCloneError |
Symbol 键的自有属性 | 被静默丢弃 |
| getter / setter | 丢失(只拷贝调用 getter 后的值) |
| 原型链 | 不保留(副本变为普通对象) |
Lodash 的 cloneDeep 是生产环境中最常用的深拷贝方案。
import _ from 'lodash'
const obj = {
a: 1,
b: { c: 2 },
date: new Date(),
regex: /test/,
func: function () { return 'hello' },
map: new Map([['key', 'value']]),
set: new Set([1, 2, 3])
}
obj.self = obj // 循环引用
const copy = _.cloneDeep(obj)
console.log(copy.date instanceof Date) // true
console.log(copy.regex instanceof RegExp) // true
console.log(copy.func()) // 'hello'
console.log(copy.map instanceof Map) // true
console.log(copy.self === copy) // true优势:
// Ramda
import { clone } from 'ramda'
const copy = clone(obj)
// immer(不可变数据)
import { produce } from 'immer'
const copy = produce(obj, draft => draft)原始对象 赋值 浅拷贝 深拷贝
┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐
│ { a: 1, │ │ { a: 1, │ │ { a: 1, │ │ { a: 1, │
│ b: { │ │ b: { │ │ b: { │ │ b: { │
│ c: 2 │ │ c: 2 │ │ c: 2 │ │ c: 2 │
│ } │ │ } │ │ } │ │ } │
│ } │ │ } │ │ } │ │ } │
└────────────┘ └────────────┘ └────────────┘ └────────────┘
↓ ↓ ↓ ↓
指向同一对象 指向同一对象 创建新对象 创建完全独立对象
修改互相影响 修改互相影响 第一层独立 所有层级独立
嵌套对象共享 嵌套对象独立以下是一个生产级别的深拷贝实现,处理了各种边界情况:
function deepClone(obj, hash = new WeakMap()) {
// 处理 null 和 undefined
if (obj === null || obj === undefined) {
return obj
}
// 处理原始类型
if (typeof obj !== 'object') {
return obj
}
// 处理循环引用
if (hash.has(obj)) {
return hash.get(obj)
}
// 处理特殊对象
if (obj instanceof Date) return new Date(obj.getTime())
if (obj instanceof RegExp) return new RegExp(obj.source, obj.flags)
if (obj instanceof Map) {
const clone = new Map()
hash.set(obj, clone)
obj.forEach((value, key) => clone.set(deepClone(key, hash), deepClone(value, hash)))
return clone
}
if (obj instanceof Set) {
const clone = new Set()
hash.set(obj, clone)
obj.forEach(value => clone.add(deepClone(value, hash)))
return clone
}
// 普通对象/数组:保留原型链和属性描述符(含不可枚举属性、getter/setter)
const clone = Array.isArray(obj) ? [] : Object.create(Object.getPrototypeOf(obj))
hash.set(obj, clone)
for (const key of Reflect.ownKeys(obj)) {
const desc = Object.getOwnPropertyDescriptor(obj, key)
if (desc.get || desc.set) {
Object.defineProperty(clone, key, desc) // 保留 getter/setter
} else {
desc.value = deepClone(desc.value, hash) // 递归深拷贝
Object.defineProperty(clone, key, desc) // 保留 enumerable/writable/configurable
}
}
return clone
}
// 测试
const source = {
arr: [1, 2, { a: 3 }],
map: new Map([['k', 'v']]),
set: new Set([1, 2]),
func: function () { return 'hello' }
}
source.self = source
Object.defineProperty(source, 'nonEnum', { value: 'non-enumerable', enumerable: false })
const cloned = deepClone(source)
console.log(cloned.arr[2].a) // 3
console.log(cloned.map instanceof Map) // true
console.log(cloned.set instanceof Set) // true
console.log(cloned.func()) // 'hello'
console.log(cloned.self === cloned) // true
console.log(cloned.nonEnum) // 'non-enumerable'以下是不同拷贝方法的性能对比测试:
// 性能测试函数
function benchmark(name, fn, obj, iterations = 10000) {
const start = performance.now()
for (let i = 0; i < iterations; i++) {
fn(obj)
}
const end = performance.now()
console.log(`${name}: ${(end - start).toFixed(2)}ms`)
}
// 测试对象
const simpleObj = { a: 1, b: 2, c: 3 }
const complexObj = {
a: 1,
b: { c: { d: [1, 2, 3] } },
list: Array.from({ length: 100 }, (_, i) => ({ id: i, name: `item-${i}` }))
}
console.log('=== 浅拷贝性能 ===')
benchmark('Object.assign()', obj => Object.assign({}, obj), simpleObj)
benchmark('扩展运算符', obj => ({ ...obj }), simpleObj)
console.log('\n=== 深拷贝性能(简单对象)===')
benchmark('JSON 方法', obj => JSON.parse(JSON.stringify(obj)), simpleObj)
benchmark('structuredClone', structuredClone, simpleObj)
benchmark('递归实现', deepClone, simpleObj)
benchmark('Lodash.cloneDeep', _.cloneDeep, simpleObj)
console.log('\n=== 深拷贝性能(复杂对象)===')
benchmark('JSON 方法', obj => JSON.parse(JSON.stringify(obj)), complexObj)
benchmark('structuredClone', structuredClone, complexObj)
benchmark('递归实现', deepClone, complexObj)
benchmark('Lodash.cloneDeep', _.cloneDeep, complexObj)性能测试结果(10000 次迭代,示意数据):
⚠️ 以下耗时为示意量级,并非权威基准——实际数值因设备、JavaScript 引擎、数据结构和浏览器版本而异,仅供比较相对快慢参考。
=== 浅拷贝性能 ===
Object.assign(): 15.23ms
扩展运算符: 12.45ms
=== 深拷贝性能(简单对象)===
JSON 方法: 45.67ms
structuredClone: 38.92ms
递归实现: 125.34ms
Lodash.cloneDeep: 98.76ms
=== 深拷贝性能(复杂对象)===
JSON 方法: 156.78ms
structuredClone: 123.45ms
递归实现: 345.67ms
Lodash.cloneDeep: 289.34ms性能结论:
| 方法 | 性能等级 | 适用场景 |
|---|---|---|
| 扩展运算符 | ⭐⭐⭐⭐⭐ | 浅拷贝首选 |
| Object.assign() | ⭐⭐⭐⭐ | 浅拷贝(合并多对象) |
| structuredClone() | ⭐⭐⭐⭐ | 深拷贝首选(现代浏览器) |
| JSON 方法 | ⭐⭐⭐ | 简单对象深拷贝 |
| Lodash.cloneDeep() | ⭐⭐⭐ | 生产环境深拷贝 |
| 递归实现 | ⭐⭐ | 需要自定义逻辑 |
| 方法 | 拷贝类型 | 函数 | 循环引用 | 特殊对象 | 不可枚举 | Symbol | 性能 |
|---|---|---|---|---|---|---|---|
| 赋值 | 引用 | ✓ | ✓ | ✓ | ✓ | ✓ | ⭐⭐⭐⭐⭐ |
| Object.assign() | 浅 | ✓ | ✓ | ✓ | ✗ | ✓ | ⭐⭐⭐⭐ |
| 扩展运算符 | 浅 | ✓ | ✓ | ✓ | ✗ | ✓ | ⭐⭐⭐⭐⭐ |
| Array.slice() | 浅 | ✓ | ✓ | ✓ | ✗ | ✓ | ⭐⭐⭐⭐ |
| JSON 方法 | 深 | ✗ | ✗ | ✗ | ✗ | ✗ | ⭐⭐⭐ |
| structuredClone() | 深 | ✗ | ✓ | ✓ | ✗ | ✗ | ⭐⭐⭐⭐ |
| 递归实现 | 深 | ✓ | ✓ | ✓ | ✓ | ✓ | ⭐⭐ |
| Lodash.cloneDeep() | 深 | ✓ | ✓ | ✓ | ✓ | ✓ | ⭐⭐⭐ |
const obj = { a: 1 }
obj.self = obj
// JSON 方法会报错
try {
JSON.parse(JSON.stringify(obj))
} catch (e) {
console.error(e) // TypeError: Converting circular structure to JSON
}
// structuredClone 可以处理
const copy1 = structuredClone(obj)
console.log(copy1.self === copy1) // true
// 递归实现需要特殊处理
const copy2 = deepClone(obj)
console.log(copy2.self === copy2) // trueconst obj = {
nan: NaN,
infinity: Infinity,
negInfinity: -Infinity,
zero: -0
}
// JSON 方法
const copy1 = JSON.parse(JSON.stringify(obj))
console.log(copy1.nan) // null(NaN 变成 null)
console.log(copy1.infinity) // null(Infinity 变成 null)
console.log(copy1.negInfinity) // null
console.log(copy1.zero) // 0(-0 变成 0)
// structuredClone
const copy2 = structuredClone(obj)
console.log(Object.is(copy2.nan, NaN)) // true(保留 NaN)
console.log(copy2.infinity) // Infinity(保留)
console.log(copy2.zero) // -0(保留)const obj = {
_value: 10,
get value() { return this._value },
set value(v) { this._value = v }
}
// 扩展运算符和 Object.assign 会调用 getter
const copy1 = { ...obj }
console.log(copy1.value) // 10
copy1.value = 20
console.log(copy1.value) // 20(变成了普通属性)
console.log(copy1._value) // 10(getter/setter 丢失)
// JSON 方法会调用 getter
const copy2 = JSON.parse(JSON.stringify(obj))
console.log(copy2.value) // 10(但 getter/setter 丢失)
// 深拷贝保留 getter/setter
const copy3 = deepClone(obj)
console.log(copy3.value) // 10
copy3.value = 20
console.log(copy3._value) // 20(getter/setter 保留)function Person(name) {
this.name = name
}
Person.prototype.greet = function () {
console.log(`Hello, ${this.name}`)
}
const person = new Person('Alice')
// JSON 方法丢失原型
const copy1 = JSON.parse(JSON.stringify(person))
console.log(copy1 instanceof Person) // false
console.log(copy1.greet) // undefined
// structuredClone 丢失原型
const copy2 = structuredClone(person)
console.log(copy2 instanceof Person) // false
// 深拷贝保留原型
const copy3 = deepClone(person)
console.log(copy3 instanceof Person) // true
copy3.greet() // 'Hello, Alice'const obj = { a: 1 }
Object.defineProperty(obj, 'b', {
value: 2,
enumerable: false
})
// 扩展运算符不复制不可枚举属性
const copy1 = { ...obj }
console.log(copy1.b) // undefined
// JSON 方法也不复制
const copy2 = JSON.parse(JSON.stringify(obj))
console.log(copy2.b) // undefined
// 深拷贝可以复制
const copy3 = deepClone(obj)
console.log(copy3.b) // 2const obj = Object.freeze({ a: 1, b: { c: 2 } })
// 拷贝后不再是冻结状态
const copy = { ...obj }
console.log(Object.isFrozen(copy)) // false
// 如果需要保持冻结状态
const frozenCopy = Object.freeze({ ...obj })
console.log(Object.isFrozen(frozenCopy)) // true// React 状态更新
import { useState } from 'react'
function TodoList() {
const [todos, setTodos] = useState([
{ id: 1, text: 'Learn React', completed: false }
])
// ❌ 错误:直接修改状态
const toggleWrong = (id) => {
const todo = todos.find(t => t.id === id)
todo.completed = !todo.completed
setTodos(todos) // 不会触发重新渲染
}
// ✅ 正确:浅拷贝创建新数组
const toggleRight = (id) => {
setTodos(todos.map(todo =>
todo.id === id
? { ...todo, completed: !todo.completed }
: todo
))
}
return <div>{/* ... */}</div>
}// 表单初始数据
const initialFormData = {
user: {
name: '',
email: '',
profile: {
age: 0,
address: ''
}
}
}
// ❌ 错误:直接使用会互相影响
const form1 = initialFormData
const form2 = initialFormData
form1.user.name = 'Alice'
console.log(form2.user.name) // 'Alice'
// ✅ 正确:使用深拷贝
const form1Copy = JSON.parse(JSON.stringify(initialFormData))
const form2Copy = JSON.parse(JSON.stringify(initialFormData))
form1Copy.user.name = 'Alice'
console.log(form2Copy.user.name) // ''const users = [
{ id: 1, name: 'Alice', age: 25 },
{ id: 2, name: 'Bob', age: 30 }
]
// ✅ 添加新用户(不修改原数组)
const newUsers = [...users, { id: 3, name: 'Charlie', age: 35 }]
// ✅ 删除用户
const filteredUsers = users.filter(u => u.id !== 2)
// ✅ 更新用户
const updatedUsers = users.map(u =>
u.id === 1 ? { ...u, age: 26 } : u
)
// ✅ 排序(slice 创建副本)
const sortedUsers = [...users].sort((a, b) => a.age - b.age)class DataCache {
constructor() {
this.cache = new Map()
}
// 存储:深拷贝防止外部修改
set(key, value) {
this.cache.set(key, structuredClone(value))
}
// 获取:深拷贝防止内部数据泄露
get(key) {
const value = this.cache.get(key)
return value ? structuredClone(value) : undefined
}
}
const cache = new DataCache()
const data = { count: 0 }
cache.set('data', data)
data.count = 10
console.log(cache.get('data').count) // 0(缓存未受影响)const defaultConfig = {
apiUrl: 'https://api.example.com',
timeout: 5000,
headers: {
'Content-Type': 'application/json'
}
}
const userConfig = {
timeout: 10000,
headers: {
'Authorization': 'Bearer token'
}
}
// 深度合并:headers 一级的对象按键合并,而不是整体覆盖
function mergeConfig(defaultConfig, userConfig) {
const result = { ...defaultConfig }
for (const [key, value] of Object.entries(userConfig)) {
if (value && typeof value === 'object' && !Array.isArray(value)) {
result[key] = mergeConfig(result[key] ?? {}, value)
} else {
result[key] = value
}
}
return result
}
const correctConfig = mergeConfig(defaultConfig, userConfig)
console.log(correctConfig.headers)
// { 'Content-Type': 'application/json', Authorization: 'Bearer token' }// ❌ 错误:使用可变对象作为默认值
function processConfig(config = { options: {} }) {
config.options.debug = true
return config
}
const result1 = processConfig()
const result2 = processConfig()
console.log(result1 === result2) // true(共享引用)
// ✅ 正确:每次创建新对象
function processConfigSafe(config) {
const defaultConfig = { options: { debug: false } }
const merged = { ...defaultConfig, ...config }
return merged
}// 简单对象,使用浅拷贝
const simpleObj = { a: 1, b: 2 }
const copy1 = { ...simpleObj }
// 复杂对象,使用深拷贝
const complexObj = { a: { b: { c: 1 } } }
const copy2 = structuredClone(complexObj)
// 包含特殊对象,使用完整深拷贝
const specialObj = { date: new Date(), func: () => {} }
const copy3 = deepClone(specialObj)
// 生产环境,使用成熟库
const productionObj = { /* ... */ }
const copy4 = _.cloneDeep(productionObj)// ❌ 避免不必要的深拷贝
function process(obj) {
// 如果只需要读取,不需要拷贝
return obj.a + obj.b
}
// ✅ 只在需要修改时拷贝
function modify(obj) {
const copy = { ...obj }
copy.a = 10
return copy
}
// ✅ 结构共享:只复制需要独立修改的层级
function updateB(obj) {
return {
...obj,
b: { ...obj.b, c: 20 } // 只深拷贝 b 一层,其余共享
}
}
const obj = { a: 1, b: { c: 2 } }
const newObj = updateB(obj)
console.log(obj.b.c) // 2
console.log(newObj.b.c) // 20
console.log(obj !== newObj) // true
console.log(obj.b !== newObj.b) // true
console.log(obj.a === newObj.a) // true(共享)// 使用 Object.freeze 防止修改
const config = Object.freeze({
apiUrl: 'https://api.example.com',
timeout: 5000
})
// config.timeout = 10000 // 严格模式下报错
// delete config.apiUrl // 严格模式下报错
// 深度冻结
function deepFreeze(obj) {
const keys = Object.keys(obj)
keys.forEach(key => {
if (typeof obj[key] === 'object' && obj[key] !== null) {
deepFreeze(obj[key])
}
})
return Object.freeze(obj)
}
const deepConfig = deepFreeze({
api: { url: 'https://api.example.com' }
})// 生产环境中监控拷贝性能
function monitoredClone(obj, name = 'object') {
const start = performance.now()
const clone = structuredClone(obj)
const duration = performance.now() - start
if (duration > 10) {
console.warn(`Slow clone detected: ${name} took ${duration.toFixed(2)}ms`)
}
return clone
}A: 核心区别在于嵌套对象的独立性:
const obj = { a: 1, b: { c: 2 } }
const shallow = { ...obj }
const deep = structuredClone(obj)
shallow.b.c = 10
deep.b.c = 20
console.log(obj.b.c) // 10(受浅拷贝影响)A: 以下场景需要深拷贝:
// 状态管理必须使用深拷贝或不可变更新
const state = { users: [{ name: 'Alice' }] }
const newState = structuredClone(state)
newState.users[0].name = 'Bob'
console.log(state.users[0].name) // 'Alice'(原状态未受影响)A: 该方法有严重限制:
const obj = {
date: new Date(),
func: () => {},
undef: undefined
}
const copy = JSON.parse(JSON.stringify(obj))
// copy.date: 字符串
// copy.func: undefined(属性被删除)
// copy.undef: undefined(属性被删除)A: 不完全可以,有限制(截至 2026-09 的主流实现):
// ❌ 错误:包含函数
const obj = { func: () => {} }
structuredClone(obj) // DataCloneError
// ✅ 正确:纯数据对象
const data = { users: [{ name: 'Alice' }] }
const copy = structuredClone(data) // OKA: 按以下流程选择:
开始
↓
需要拷贝吗?
├─ 否 → 直接使用引用
└─ 是 ↓
有嵌套对象吗?
├─ 否 → 浅拷贝(扩展运算符)
└─ 是 ↓
包含函数/特殊对象吗?
├─ 否 → structuredClone()
├─ 是 → deepClone() 或 Lodash
└─ 需要兼容旧浏览器?
├─ 是 → Lodash 或 JSON 方法
└─ 否 → structuredClone()A: JSON 格式不支持循环引用,会导致无限递归:
const obj = { a: 1 }
obj.self = obj
// JSON.stringify 会无限尝试序列化
// obj -> obj.self -> obj.self.self -> ...
JSON.stringify(obj) // TypeError: Converting circular structure to JSON
// 解决方案
// 1. 使用 structuredClone(自动处理)
const copy1 = structuredClone(obj)
// 2. 使用 WeakMap 缓存(递归实现)
const copy2 = deepClone(obj)
// 3. 移除循环引用
const safeObj = { ...obj }
delete safeObj.selfA: 选择性深拷贝某些属性:
function partialDeepClone(obj, deepKeys = []) {
const result = { ...obj }
deepKeys.forEach(key => {
if (result[key] !== undefined) {
result[key] = structuredClone(result[key])
}
})
return result
}
const obj = {
a: 1,
b: { c: 2 },
d: { e: 3 }
}
// 只对 b 属性进行深拷贝
const copy = partialDeepClone(obj, ['b'])
copy.b.c = 10
copy.d.e = 20
console.log(obj.b.c) // 10(受影响)
console.log(obj.d.e) // 20(受影响)A: 使用严格相等检查:
function isDeepCopied(original, copy) {
// 基本检查
if (original === copy) return false
// 递归检查所有嵌套对象
function checkDeep(orig, cp) {
for (const key in orig) {
if (typeof orig[key] === 'object' && orig[key] !== null) {
if (orig[key] === cp[key]) return false
if (!checkDeep(orig[key], cp[key])) return false
}
}
return true
}
return checkDeep(original, copy)
}
const obj = { a: 1, b: { c: 2 } }
const shallowCopy = { ...obj }
const deepCopy = structuredClone(obj)
console.log(isDeepCopied(obj, shallowCopy)) // false
console.log(isDeepCopied(obj, deepCopy)) // trueA: 会影响,优化建议:
// ❌ 性能差:每次都深拷贝
function updateState(state, updates) {
const newState = structuredClone(state)
Object.assign(newState, updates)
return newState
}
// ✅ 性能好:使用 immer
import { produce } from 'immer'
function updateStateOptimized(state, updates) {
return produce(state, draft => {
Object.assign(draft, updates)
})
}A: 使用泛型和类型守卫:
// 深拷贝函数类型定义
function deepClone<T>(obj: T, hash = new WeakMap()): T {
if (obj === null || typeof obj !== 'object') {
return obj
}
if (hash.has(obj)) {
return hash.get(obj)
}
// ... 实现代码
return clone as T
}
// 使用示例
interface User {
name: string
profile: {
age: number
}
}
const user: User = {
name: 'Alice',
profile: { age: 25 }
}
const copy: User = deepClone(user)
copy.profile.age = 26
console.log(user.profile.age) // 25(类型安全)| 场景 | 推荐方法 | 原因 |
|---|---|---|
| 简单对象 | {...obj} | 简洁高效 |
| 合并对象 | Object.assign() | 支持多对象合并 |
| 数组拷贝 | [...arr] 或 arr.slice() | 性能最优 |
| 纯数据深拷贝 | structuredClone() | 原生支持,性能好 |
| 包含函数 | Lodash.cloneDeep() | 功能全面 |
| 生产环境 | Lodash 或 immer | 稳定可靠 |
赋值 > 浅拷贝 > structuredClone > JSON 方法 > Lodash > 自定义递归structuredClone() 是现代浏览器首选