manlili blog

JS的浅拷贝与深拷贝研究

JS数据类型可以分为(ES5,暂时不考虑ES6):
(1)简单数据类型:Number、String、undefined、boolean、null
(2)复杂数据类型:Object、Array
简单的数据类型,往往是赋值操作,而复杂数据类型是引用操作。

深浅拷贝原理

浅拷贝原理

1
2
3
4
5
6
7
8
9
10
11
12
var obj ={a:1,b:2,c:[1,2]};
var shallowCopy = shallow(obj);
function shallow(obj){
var shallowObj = {};
for(var name in obj){
if(obj.hasOwnProperty(name)){
shallowObj[name] = obj[name]
}
}
return shallowObj
}
console.log(shallowCopy);//输出的就是这个对象,我们实现了简单的浅复制;

浅拷贝:只会将对象的各个属性进行依次复制,并不会进行递归复制,而js存储对象都是存地址的,所以浅复制会导致obj.c和shallowCopy.c 指向同一块内存地址;会导致引用。

深拷贝原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function deepClone(obj){
var newObj = obj.constructor === Array ? []:{};
if(typeof obj !== 'object'){
return
}else{
for(var i in obj){
var prop = obj[i];
if(prop === obj) { //// 避免相互引用对象导致死循环
continue;
}
if(obj.hasOwnProperty(i)){
if (typeof obj[i] === 'object') {
newObj[i] = deepClone(obj[i]);
}else {
newObj[i] = obj[i];
}
}
}
}
return newObj
}
`

深复制:它不仅将原对象的各个属性逐个复制出去,而且将原对象各个属性所包含的对象也依次采用深复制的方法递归复制到新对象上,最核心的思想还是采用递归的方式,不断进行,直到基本数据类型后,再复制。

数组的深浅拷贝

数组浅拷贝

1
2
3
4
5
var arr1 = [1, 2, 3, 4, 5];
var arr2 = arr1;
arr1[0] = "a";
console.log(arr1);// ["a", 2, 3, 4, 5]
console.log(arr2);// ["a", 2, 3, 4, 5]

当改变arr1数组中的元素的时候,arr2也会改变,如何避免上面的情况发生呢那就是深拷贝了。

数组深拷贝

方法一:slice方法

1
2
3
4
5
var arr1 = [1, 2, 3, 4, 5];
var arr2 = arr1.slice(0);
arr1[0] = "a";
console.log(arr1);//["a", 2, 3, 4, 5]
console.log(arr2);//[1, 2, 3, 4, 5]

方法二:concat方法

1
2
3
4
5
var arr = ["One","Two","Three"];
var arrtooo = arr.concat();
arrtooo[1] = "set Map To";
document.writeln("数组的原始值:" + arr + "<br />");//Export:数组的原始值:One,Two,Three
document.writeln("数组的新值:" + arrtooo + "<br />");//Export:数组的新值:One,set Map To,Three

方法三:最简单的深拷贝实现方式
缺点:原型链没了,对象就是object,所属的类没了。

1
2
3
4
var arr1 = [1, 2, 3, 4, 5];
var arr2 = JSON.parse(JSON.stringify(arr1));
console.log(arr2);//[1, 2, 3, 4, 5];
console.log(arr1 === arr2);//false

序列化然后反序列化重新生成一个新的数组(引用对象)。

对象的深浅拷贝

对象浅拷贝

法一:

1
2
3
4
5
6
7
8
9
10
11
var obj_a = {
name: "dqhan",
age: 25,
sex: "male",
hobby: {
1: "eat",
2: "play"
}
}
var obj_b = obj_a;
obj_b.age = 18;

图

法二:Object.assign()
Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是 Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。

1
2
3
4
5
6
7
8
9
10
11
12
var x = {
a: 1,
b: 2,
c: 3
};
var y = Object.assign({}, x);
console.log(y.b === x.b); // true
var obj = { a: {a: "hello", b: 21} };
var initalObj = Object.assign({}, obj);
initalObj.a.a = "changed";
console.log(obj.a.a); // "changed"

对象深拷贝

法一:

1
2
3
4
5
var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = { a: obj1.a, b: obj1.b, c: obj1.c };
obj2.b = 100;
console.log(obj1); // { a: 10, b: 20, c: 30 } <-- 沒被改到
console.log(obj2); // { a: 10, b: 100, c: 30 }

法二:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var obj_a = {
name: "dqhan",
age: 25,
sex: "male",
hobby: {
1: "eat",
2: "play"
}
}
function deepClone(obj){
var newObj = obj.constructor === Array ? []:{};
if(typeof obj !== 'object'){
return
}else{
for(var i in obj){
if(obj.hasOwnProperty(i)){
newObj[i] = typeof obj[i] === 'object'? deepClone(obj[i]) : obj[i];
}
}
}
return newObj
}

法三:Object.create()
Object.create() 方法使用指定的原型对象和其属性创建了一个新的对象。

1
2
3
4
var a = {x:1};
var b = Object.create(a);
console.log(b);//输出:{};
console.log(b.__proto__); //输出:{x:1}

上面这句话说明了b的原型指向a的prototype属性。

法四:new object()

1
2
3
如果用 b =new object(a)
connsole.log(b);//输出:{x:1}
congsole.log(b.__proto__);//输出:{}

请我喝杯果汁吧!