什么是拷贝?为什么需要它?
在 JS 中,当我们把一个对象赋值给另一个变量时,实际上只是复制了对象的引用(地址),而不是对象本身。这就像是两个人拿着同一把钥匙,指向同一个房间。
const person1 = { name: '小明', age: 25 }
const person2 = person1
person2.name = '小红'
console.log(person1.name) // 输出 '小红'
看到了吗?我们只改了 person2
,但 person1
也变了,因为它们指向同一个对象。有时这不是我们想要的结果,这就需要拷贝出来一个新对象。

浅拷贝
浅拷贝好比是复印了对象的"第一层"。它会创建一个新对象,但只复制原对象第一层属性的值。如果属性是基本类型(数字、字符串等),会复制值;如果是引用类型(对象、数组),则只复制引用地址。
浅拷贝的常用方法:
// 方法一:Object.assign()
const original = { name: '小明', hobbies: ['跑步', '游泳'] }
const copy = Object.assign({}, original)
// 方法二:展开运算符
const copy2 = { ...original }
// 方法三:数组的浅拷贝
const array = [1, 2, { a: 3 }]
const arrayCopy = [...array]
// 或者
const arrayCopy2 = array.slice()
看看浅拷贝的效果:
original.name = '小红'
original.hobbies.push('篮球')
console.log(copy.name) // '小明',没变,因为是基本类型
console.log(copy.hobbies) // ['跑步', '游泳', '篮球'],变了!因为是引用类型
深拷贝
深拷贝则是彻底的复制,会递归地复制所有层级的属性,创建一个全新的对象,两者完全独立,互不影响。
深拷贝的实现方法:
// 方法一:JSON 转换(有局限性)
const original = { name: '小明', hobbies: ['跑步', '游泳'] }
const copy = JSON.parse(JSON.stringify(original))
// 方法二:手写递归函数
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') return obj
const result = Array.isArray(obj) ? [] : {}
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
result[key] = deepClone(obj[key])
}
}
return result
}
const copy2 = deepClone(original)
// 方法三:使用第三方库如 lodash
// const copy3 = _.cloneDeep(original)
测试一下效果:
original.name = '小红'
original.hobbies.push('篮球')
console.log(copy.name) // '小明',没变
console.log(copy.hobbies) // ['跑步', '游泳'],也没变!
两者的区别和选择
- 浅拷贝:性能好,但只能处理第一层属性
- 深拷贝:完全独立,但性能消耗大
选择哪种方式取决于你的需求:
- 如果对象结构简单,只有一层,或者你只关心第一层属性的独立性,用浅拷贝就够了
- 如果需要完全独立的对象副本,就需要深拷贝
注意事项
JSON.parse(JSON.stringify())
这种深拷贝方法有限制:
- 不能处理函数、undefined、Symbol、循环引用
- 会丢失原型链
- 无法正确处理 Date、RegExp 等特殊对象
- 递归实现深拷贝要注意防止循环引用,避免栈溢出