偶然与道友产生了深浅拷贝的异议,所以赶紧补一补习,需要的你也可以看看
浅拷贝
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原
对象。
举个栗子:假如有两个对象a和b,我想让b对象拥有a对象的属性和方法,然后b对象某些同属性的keyvalue和a不一样,可能会这么写1
2
3
4
5
6var a = {
name: "hhardyy"
}
var b = a;
b.name = "xiaofangkuai";
console.log(a.name) //xiaofangkuai
但是这样之后a对象的name也被改变了,举例浅拷贝一个initObj对象,里面有各种属性和方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34var initObj = {
a: "hhardyy",
b: {
a: "xiaofangkuai"
},
c: ["1", "2", "3"],
d: function () {
alert("hhardyy");
}
}
function clone(obj){
var newObj={};
for(key in obj){
newObj[key]=obj[key]
}
return newObj;
}
var newObj = clone(initObj);
console.log('拷贝的对象属性方法')
console.log(newObj.b);
console.log(newObj.c);
console.log(newObj.d);
newObj.a="xiaofangkuai";
newObj.b.a = "hhardyy";
newObj.c = [1, 2, 3];
newObj.d = function () {
alert("hhardyy1");
};
console.log('初始的对象属性方法')
console.log(initObj.b);
console.log(initObj.c);
console.log(initObj.d);
console.log('区别是initObj下面的b对象下面a没有被拷贝,也就是说只拷贝了一层')
ES6提供了一个新函数用于浅拷贝Object.assign(),可以处理一层的深度拷贝,可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。拷贝的是对象的属性的引用,而不是对象本身。
文档地址1
Object.assign(target, ...sources) //target=目标对象,sources=源对象,返回值等于目标对象
继续上面的例子,这是一层的1
2
3
4
5
6var a = {
name: "hhardyy"
}
var b = Object.assign({}, a);
b.name="xiaofangkuai"
console.log(a.name)//hhardyy,证明a.name没有被改动
继续上面的例子1
2
3
4
5
6
7
8
9
10
11
12
13var a = {
name: {
'firstname':'xiao',
'lastname':{
age:20
}
}
}
var b = Object.assign({}, a);
b.name.firstname="hhardyy"
b.name.lastname.age=19
console.log(b.name)
console.log(a.name)
深拷贝
obj1要完全复制obj2的属性方法,同时obj1要是改变不会影响obj2的属性方法,同时满足多层拷贝
对象只有一层的话,可以用ES6的Object.assign(),例子还是上面那个
stringify
1 | var a = { |
这种深拷贝不用每层递归,也比较简单,但是这种方法也有不少坏处,IE6、7不兼容,兼容的方法也简单,勉强用evel或者去json的官网下载json类下面
的json2.js,然后引入,就可以兼容了。同时这种方法会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。也就是说
它会干掉原来对象里头的构造函数,所以有undefined和function会被除掉,只有可以转成JSON格式的对象才可以这样用,像function没办法转成JSON。还是那个例子1
2
3
4
5
6
7
8
9
10
11
12
13
14
15var a = {
name: {
'firstname':'xiao',
'lastname':{
age:20
}
},
func(){
console.log('hhardyy')
}
}
var strA=JSON.stringify(a);
var b =JSON.parse(strA)
console.log("console.log(typeof a.func):"+typeof a.func)
console.log("console.log(typeof b.func):"+typeof b.func)
stringify的缺点就是:无法实现对函数、RegExp等特殊对象的克隆,会抛弃对象的construct,所有的构造函数会指Object,有循环引用,会报错
递归拷贝
1 | var a = { |
JQ的$.extend
1 | var $ = require('jquery'); |
函数库lodash的_.cloneDeep
1 | var _ = require('lodash'); |
这个用起来简单,而且性能也不错
封装一个对象拷贝的方法
假如obj2要拷贝obj1里面的属性和方法可以用1
2
3
4
5
6function HexTend(obj1,obj2){
for(var attr in obj1){
obj2[attr]=obj1[attr];
}
return obj2;
}
也可以简单点b={…a}或者Object.assign(b,a),如果非对象参数出现在源对象的位置,那么这些参数都会转成对象,如果无法转成对象,就会跳过。也就是undefined和null不放
在第一个参数,就不会报错。其他类型的值比如number、string、boolean不在第一个参数,也不会报错。但是字符串会以数组形式拷贝入目标对象,其他值没效果。
注意:Object.assign方法实行的是浅拷贝,如果源对象某个属性的值是对象,则目标对象拷贝到的是这个对象的引用。