在js中,数组和对象的复制如果使用=
号来进行复制,那只是浅拷贝。如下图演示:
如上,arr
的修改,会影响arr2
的值,这显然在绝大多数情况下,并不是我们所需要的结果。
因此,数组以及对象的深拷贝就是javascript
的一个基本功了。
对象:
一. 合并与克隆的差别
1. 克隆是特殊的合并(以空对象作为目标对象,非空对象作为源对象进行合并),克隆要求目标对象与源对象的 constructor相同。
2. 克隆的源对象只有一个,合并的源对象可以是多个。
二. 合并的方法
1.Object.assign():
例:var obj1 ={
m : 1,
n : 2,
j : {
r : {
h : 2
},
p : 4 } ,
p : 1
}
var obj2 ={ m : 2 , n : undefined, j : { h : 2 ,o: 3}}
var obj3 = Object.assign(obj1,obj2);
结果:obj1 = { m : 2,n : undefined, j : { h : 2, o : 3 }, p : 1 };
obj2 ={ m : 2 , n : undefined, j : { h : 2, o : 3 } };
obj3 ={ m : 2 , n : undefined, j : { h : 2, o : 3 }, p : 1 };
注意:1). 目标对象自身也会变 obj1===obj3
2). 此方法为浅合并
3). undefined参与合并
4). 原型不属性参与合并
2. $.extend:
情况一:
例:var obj1 ={ m : 1, n : 2, j : { r : { h : 2 }, p : 4 }, p : 1 }
var obj2 ={ m : 2, n : undefined, j : { h : 2, o : 3 } }
var obj3 = $.extend(obj1,obj2);
结果:obj1 = { m : 2, n : 2, j : { h : 2, o : 3 }, p : 1 };
obj2 ={ m : 2, n : undefined, j : { h : 2, o : 3 } };
obj3 ={ m : 2, n : 2, j : { h : 2, o : 3 }, p : 1 };
注意:1). 目标对象自身也会变 obj1===obj3
2). 此方法为浅合并
3). undefined不参与合并
4). 原型不属性参与合并
- 如果目标对象与源对象有同名属性,则后面的属性会覆盖前面的属性
- 如果只有一个参数,则直接返回该参数。即
Object.assign(obj) === obj
- 如果第一个参数不是对象,而是基本数据类型(Null、Undefined除外),则会调用对应的基本包装类型
- 如果第一个参数是Null和Undefined,则会报错;如果Null和Undefined不是位于第一个参数,则会略过该参数的复制
情况二:
例:var obj1 ={ m : 1, n : 2, j : { r : { h : 2 }, p : 4 }, p : 1 }
var obj2 ={ m : 2, n : undefined, j : { h : 2, o : 3 } }
var obj4 = $.extend({},obj1,obj2);
结果:obj1 = { m : 1, n : 2, j : { r : { h : 2 }, p : 4 }, p : 1 };
obj2 ={ m : 2, n : undefined, j : { h : 2, o : 3 } };
obj3 ={ m : 2, n : 2, j : { h : 2, o : 3 }, p : 1 };
注意:1). 此方法为浅合并
2). undefined不参与合并
3). 原型不属性参与合并
情况三:
例:var obj1 ={ m:1,n:2,j:{r:{h:2},p:4},p:1}
var obj2 ={m:2,n:undefined,j:{h:2,o:3}}
var obj3 = $.extend(true,obj1,obj2);
结果:obj1 = {m:2,n:2,j:{h:2,o:3,r:{h:2},p:4},p:1};
obj2 ={m:2,n:undefined,j:{h:2,o:3}};
obj3 ={m:2,n:2,j:{h:2,o:3,r:{h:2},p:4},p:1};
注意:1). 目标对象自身也会变 obj1===obj3
2). 此方法为深合并
3). undefined不参与合并
4). 原型不属性参与合并
3. 遍历赋值法
思路:将obj2中存在的属性但obj1不存在的属性赋值给obj1。
步骤:1). 遍历obj2中属性。
2). 判断obj1不存在此属性
3). 将次值赋给obj1
var extentObj = function(obj1,obj2){ for(let key in obj2){ if(obj2.hasOwnProperty(key) && (!obj1.hasOwnProperty(key))){ obj1[key] = obj2[key] } } }
4、扩展运算符实现对象的深拷贝
var obj = { name: 'FungLeo', sex: 'man', old: '18' } var { ...obj2 } = obj obj.old = '22' console.log(obj) console.log(obj2)
运行结果如下:
三. 克隆的方法
1. JSON.parse(JSON.stringify()):
1). 先将对象变为字符串,然后再变为json对象,防止对象的指针指向问题,为深拷贝
2). undefined 和 function 类型的属性会被忽略,而 Date 类型的属性则会被转换为字符串
2. $.extend:
传true为深拷贝,不传为浅拷贝
注意:深拷贝与浅拷贝的区别
浅拷贝直接把引用地址原样拿来,此时,不管源对象还是目标对象,修改引用属性后另一个对象的同名属性都会受到影响。
深拷贝则会递归地在目标对象上创建值,目标对象和源对象之间将完全独立
数组:
一、 for循环实现数组的深拷贝
for循环是非常好用的。如果不知道高级方法,通过for循环能够完成我们大多数的需求。
var arr = [1,2,3,4,5] var arr2 = copyArr(arr) function copyArr(arr) { let res = [] for (let i = 0; i < arr.length; i++) { res.push(arr[i]) } return res }
如上,通过对数组的for循环,即可实现对数组的深拷贝了。
二、 slice方法实现数组的深拷贝
这个代码实现非常简单。原理也比较好理解,他是将原数组中抽离部分出来形成一个新数组。我们只要设置为抽离全部,即可完成数组的深拷贝。代码如下:
var arr = [1,2,3,4,5] var arr2 = arr.slice(0) arr[2] = 5 console.log(arr) console.log(arr2)
运行结果如下:
三、concat 方法实现数组的深拷贝
这个代码也非常简单,原理更加粗暴。它是用于连接多个数组组成一个新的数组的方法。那么,我们只要连接它自己,即可完成数组的深拷贝。代码如下:
var arr = [1,2,3,4,5] var arr2 = arr.concat() arr[2] = 5 console.log(arr) console.log(arr2)
运行结果如下:
四、ES6扩展运算符实现数组的深拷贝
OK,以上之前讲的方法全部过时了,用下面的方法实现数组的深拷贝是最简单的。
var arr = [1,2,3,4,5] var [ ...arr2 ] = arr arr[2] = 5 console.log(arr) console.log(arr2)
运行结果如下: