区别: 浅拷贝只是增加了一个指针指向已存在的内存地址,仅仅是指向被复制的内存地址,如果原地址发生改变,那么浅拷贝复制出来的对象也会相应的改变。深拷贝是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存。
浅拷贝
一、JavaScript
存储对象都是存地址的,所以浅拷贝会导致 a
和 b
指向同一块内存地址。
数组的复制其实相当于复制了索引,改变其中一个变量,其他引用也会随之改变。
var a = [1,2,3];
var b = a;
b[0] = 4;
// a 为 4 2 3
// b 为 4 2 3
var a = [1,2,3];
var b = a;
b[0] = 4;
// a 为 4 2 3
// b 为 4 2 3
当传递一个对象(在 javascript
里数组不是简单数据类型,而是对象)到一个函数,如果在函数内部改变了参数的内容,在函数外部这个变化是可见。
var a = [1,2,3];
function test(a){
a[0]=4;
return a;
}
test(a);
// a 为 [4,2,3]
二、Object.assign()
方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。
案例一:
const obj1 = {a: {b: 1}};
const obj2 = Object.assign({}, obj1);
obj1.a.b = 2;
obj2.a.b // 2
案例二:
var person = {
id: '123',
name: {
firstName:'zhang',
lastName: 'san'
},
sex: 'man',
}
var person2 = Object.assign({}, person)
person2.name.lastName = 'qiang'
/*
person 为 {
id: '123',
name: {
firstName:'zhang',
lastName: 'qiang'
},
sex: 'man',
}
person2 为 {
id: '123',
name: {
firstName:'zhang',
lastName: 'qiang'
},
sex: 'man',
}
*/
上面代码中,源对象 obj1
的 a
属性的值是一个对象,Object.assign()
拷贝得到的是这个对象的引用。这个对象的任何变化,都会反映到目标对象上面。
深拷贝
slice
函数
concat
函数
这两个函数的原理都是返回数组的一个副本(相当于另外开辟内存空间),所以不会改变数组本身的值。
通常情况下,我们也会采用 JSON.parse(JSON.stingify(obj))
方法深拷贝。
原理:利用 JSON.stingify
将js 对象序列化(JSON字符串
),再使用 JSON.parse
来反序列化(还原)js
对象。但以下几点不适用该方法:
- 如果
obj
里面有时间对象,则 JSON.stringify
后再 JSON.parse
的结果,时间将只是字符串的形式。而不是时间对象;
- 如果
obj
里有 RegExp
、Error
对象,则序列化的结果将只得到空对象;
- 如果
obj
里有函数,undefined
,则序列化的结果会把函数或 undefined
丢失;
- 如果
obj
里有 NaN
、Infinity
和 -Infinity
,则序列化的结果会变成 null
;
-
JSON.stringify()
只能序列化对象的可枚举的自有属性,例如 如果 obj
中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))
深拷贝后,会丢弃对象的 constructor
;
- 如果对象中存在循环引用的情况也无法正确实现深拷贝;
参考文章