實(shí)現(xiàn)深拷貝的多種方式

來(lái)源 |?http://www.fly63.com/article/detial/9650
淺拷貝主要拷貝的是對(duì)象的引用值,當(dāng)改變對(duì)象的值,另一個(gè)對(duì)象的值也會(huì)發(fā)生變化。?
1.簡(jiǎn)單深拷貝(一層淺拷貝)
①for循環(huán)拷貝
// 只復(fù)制第一層的淺拷貝function simpleCopy(obj1) {var obj2 = Array.isArray(obj1) ? [] : {};for (let i in obj1) {obj2[i] = obj1[i];}return obj2;}var obj1 = {a: 1,b: 2,c: {d: 3}}var obj2 = simpleCopy(obj1);obj2.a = 3;obj2.c.d = 4;alert(obj1.a); // 1alert(obj2.a); // 3alert(obj1.c.d); // 4alert(obj2.c.d); // 4
②Object.assign()實(shí)現(xiàn)一層深拷貝
var obj1 = {a: 1,b: 2,c: 3}var obj2 = Object.assign({}, obj1);obj2.b = 5;console.log(obj1.b); // 2console.log(obj2.b); // 5var obj1 = {a: 1,b: 2,c: ['a','b','c']}var obj2 = Object.assign({}, obj1);obj2.c[1] = 5;console.log(obj1.c); // ["a", 5, "c"]console.log(obj2.c); // ["a", 5, "c"]
③slice實(shí)現(xiàn)
// 對(duì)只有一級(jí)屬性值的數(shù)組對(duì)象使用slicevar a = [1,2,3,4];var b = a.slice();b[0] = 2;alert(a); // 1,2,3,4alert(b); // 2,2,3,4// 對(duì)有多層屬性的數(shù)組對(duì)象使用slicevar a = [1,[1,2],3,4];var b = a.slice();b[1][0] = 2;alert(a); // 1,2,2,3,4alert(b); // 1,2,2,3,4
④使用concat()方法
var a=[1,2,[3,4]]var c=[];var b=c.concat(a);b[0]=5;b[2][0]=6;console.log(b[0]);//5console.log(a[0])//1console.log(b[2][0]);//6console.log(a[2][0])//6
⑤es6的擴(kuò)展運(yùn)算符"..."
var a=[1,2,[3,4]]var b=[...a];b[0]=5;b[2][0]=6console.log(b[0]);//5console.log(a[0])//1console.log(b[2][0]);//6console.log(a[2][0])//6
⑥通過(guò)Object.create()實(shí)現(xiàn)
function deepCopy(obj) {var copy = Object.create(Object.getPrototypeOf(obj));var propNames = Object.getOwnPropertyNames(obj);propNames.forEach(function(name) {var desc = Object.getOwnPropertyDescriptor(obj, name);Object.defineProperty(copy, name, desc);});return copy;}var obj1 = { a: 1, b: {bc: 50, dc: 100, be: {bea: 1}} };var obj2 = deepCopy(obj1);obj2.a = 20;obj2.b.bc = 60;console.log(obj1.a)//1console.log(obj2.a)//20console.log(obj1.b.bc)//60console.log(obj2.b.bc)//60
2.粗暴深拷貝(拋棄對(duì)象的constructor)
使用jsON.stringify和jsON.parse實(shí)現(xiàn)深拷貝:JSON.stringify把對(duì)象轉(zhuǎn)成字符串,再用JSON.parse把字符串轉(zhuǎn)成新的對(duì)象;
function deepCopy(obj1){let _obj = JSON.stringify(obj1);let obj2 = JSON.parse(_obj);return obj2;}var a = [1, [1, 2], 3, 4];var b = deepCopy(a);b[1][0] = 2;alert(a); // 1,1,2,3,4alert(b); // 2,2,2,3,4
缺陷:它會(huì)拋棄對(duì)象的constructor,深拷貝之后,不管這個(gè)對(duì)象原來(lái)的構(gòu)造函數(shù)是什么,在深拷貝之后都會(huì)變成Object;這種方法能正確處理的對(duì)象只有 Number, String, Boolean, Array, 扁平對(duì)象,也就是說(shuō),只有可以轉(zhuǎn)成JSON格式的對(duì)象才可以這樣用,像function沒(méi)辦法轉(zhuǎn)成JSON;
let obj1 = {fun:function(){alert(123);}}let obj2 = JSON.parse(JSON.stringify(obj1));console.log(typeof obj1.fun); // functionconsole.log(typeof obj2.fun); // undefined
3.復(fù)雜深拷貝(相對(duì)完美)
遞歸拷貝實(shí)現(xiàn)深拷貝,解決循環(huán)引用問(wèn)題
/*** 判斷是否是基本數(shù)據(jù)類型* @param value*/function isPrimitive(value){return (typeof value === 'string' ||typeof value === 'number' ||typeof value === 'symbol' ||typeof value === 'boolean')}/*** 判斷是否是一個(gè)js對(duì)象* @param value*/function isObject(value){return Object.prototype.toString.call(value) === "[object Object]"}/*** 深拷貝一個(gè)值* @param value*/function cloneDeep(value){// 記錄被拷貝的值,避免循環(huán)引用的出現(xiàn)let memo = {};function baseClone(value){let res;// 如果是基本數(shù)據(jù)類型,則直接返回if(isPrimitive(value)){return value;// 如果是引用數(shù)據(jù)類型,我們淺拷貝一個(gè)新值來(lái)代替原來(lái)的值}else if(Array.isArray(value)){res = [...value];}else if(isObject(value)){res = {...value};}// 檢測(cè)我們淺拷貝的這個(gè)對(duì)象的屬性值有沒(méi)有是引用數(shù)據(jù)類型。如果是,則遞歸拷貝Reflect.ownKeys(res).forEach(key=>{if(typeof res[key] === "object" && res[key]!== null){//此處我們用memo來(lái)記錄已經(jīng)被拷貝過(guò)的引用地址。以此來(lái)解決循環(huán)引用的問(wèn)題if(memo[res[key]]){res[key] = memo[res[key]];}else{memo[res[key]] = res[key];res[key] = baseClone(res[key])}}})return res;}return baseClone(value)}
4.ES插件lodash
import lodash from 'lodash'var objects = [1,{ 'a': 1 }, { 'b': 2 }];var deep = lodash.cloneDeep(objects);deep[0] = 2;deep[1].a = 2;console.log(objects[0]);//1console.log(deep[0]);//2console.log(objects[1].a);//1console.log(objects[1].a);//2

評(píng)論
圖片
表情
