js中对于基本类型数据的拷贝(复制)就是值本身,而对于引用类型数据,则分为深拷贝和浅拷贝。
浅拷贝
js对象浅拷贝:拷贝的是其引用地址,也就是说,如果原地址中对象被改变了, 那么浅拷贝出来的对象也会相应改变
let obj1 = {a:1};
let obj2 = obj1;
obj2.a = 2;
console.log(obj1); //{a:2}
console.log(obj2); //{a:2}
复制代码
深拷贝
引用类型数据的深拷贝在计算机中开辟了一块新的内存地址用于存放复制的对象,如果原地址中对象被改变了, 那么深拷贝出来的对象不会相应改变。
一、只能处理一层数据结构的深拷贝:
- 1、数组:slice concat ...运算符
- 注意:splice也能得到新数组,但splice操作会影响原数组
let arr1 = [2,{name:"123"},3,4,5];
let arr2 = arr1.slice();
arr2[0] = 9;
arr2[1].name = 999;
console.log(arr1);//[ 2, { name: 999 }, 3, 4, 5 ]
console.log(arr2);//[ 9, { name: 999 }, 3, 4, 5 ]
复制代码
上述例子中,分别改变数组第一项的值与第二项对象中name的值,打印出来的结果中arr1中的第一项未被修改,而第二项中的name被修改,说明slice只能拷贝单一层次结构的数据。
- 2、对象{}:...运算符 Object.assign Object.create
- 注意:Object.create(obj)的返回值是一个空对象,其__proto__指向了被拷贝的对象obj
let obj1 = {name:"qiankaobei",other:{age:"123",height:"321"}}; let obj2 = Object.assign({},obj1); obj2.name = 'yiceng'; obj2.other.age = '999'; console.log(obj1);//{ name: 'qiankaobei', other: { age: '999', height: '321' } } console.log(obj2);//{ name: 'yiceng', other: { age: '999', height: '321' } } 复制代码
单独说下Object.create()
let obj11 = {name:"shenkaobei",other:{age:"123"}}; let obj22 = Object.create(obj11); console.log(obj11);//{ name: 'shenkaobei', other: { age: '123' } } console.log(obj22);//{} 返回一个空对象 console.log(obj22.name);//shenkaobei 能取到name值 console.log(obj22.other.age);//123 console.log(obj22.__proto__);//{ name: 'shenkaobei', other: { age: '123' } } obj22的__proto__指向了被拷贝对象 复制代码
至此我们知道,create返回一个空对象,并且这个空对象的__proto__指向了被拷贝对象,所以obj22能取到相应的属性值,接下来我们继续写:
obj22.name = "888"; obj22.other.age = "888"; console.log(obj11);//{ name: 'shenkaobei', other: { age: '888' } } name没变 age被改变 console.log(obj22);//{ name: '888' } 给obj22添加了name属性 console.log(obj22.name);//888 取自obj22 console.log(obj22.other.age);//888 取自原型 //obj22.other与obj11.other指向同一个空间 console.log(obj22.__proto__);//{ name: 'shenkaobei', other: { age: '888' } } 复制代码
由此可见,实际上Object.object()采用的是继承原理,其第二个参数还可以设置属性描述符来达到进一步的应用,大家可以网搜一下
- 3、当然也可以用 for for in for of 等系列循环
二、多层数据结构的深拷贝:
- 1、JSON.parse(JSON.stringify(obj1))
- 注意:会抛弃对象之前的constructor,拷贝之后指向Object,不能处理 Function、Date、RegExp等对象
let arr_j = [function () {}, /\w/]; let obj_j = {fn:function () {},reg:/\w/}; let arr_p = JSON.parse(JSON.stringify(arr_j)); let obj_p = JSON.parse(JSON.stringify(obj_j)); console.log(arr_p);//[ null, {} ] console.log(obj_p);//{ reg: {} } 复制代码
由此可见,Function会被解析成null,RegExp被解析成{}
- 2、递归拷贝: 创建一个新对象 + for in + 递归
let obj_1 = {name:"shenkaobei",other:{age:"123",height:"321"}}; function deepClone(obj) { if(obj == null)return; if(typeof obj !="object")return obj; if(obj instanceof RegExp)return new RegExp(obj); if (obj instanceof Date) return new Date(obj); let newObj = new obj.constructor();//创建相同类型的空对象 for (const key in obj) { newObj[key] = deepClone(obj[key]) } return newObj } let obj_2 = deepClone(obj_1); obj_2.other.age = "888";//改变第二层数据的值 console.log(obj_1);//{ name: 'shenkaobei', other: { age: '123', height: '321' } } console.log(obj_2);//{ name: 'shenkaobei', other: { age: '888', height: '321' } } 复制代码
- 3、借助第三方库或插件
- lodash: _.cloneDeep(obj1)
- jq: $.extend(true, {}, obj1);
具体用法大家可以网搜其API,在此就先不做过多阐述了