JS实现深拷贝

  • A+
所属分类:Web前端
摘要

使用JSON.stringify和JSON.parse实现深拷贝:JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象;


目录

  • JS实现
    • 简单深拷贝(一层浅拷贝)
    • 粗暴深拷贝(抛弃对象的constructor)
    • 复杂深拷贝(相对完美)
  • ES实现
    • lodash中的cloneDeep方法(完美)

一.JS实现

1.简单深拷贝(一层浅拷贝)

①for循环拷贝

// 只复制第一层的浅拷贝 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); // 1 alert(obj2.a); // 3 alert(obj1.c.d); // 4 alert(obj2.c.d); // 4 

②Object.assign()实现一层深拷贝

 var obj1 = {     a: 1,     b: 2,     c: 3 } var obj2 = Object.assign({}, obj1); obj2.b = 5; console.log(obj1.b); // 2 console.log(obj2.b); // 5 
 var 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实现

// 对只有一级属性值的数组对象使用slice var a = [1,2,3,4]; var b = a.slice(); b[0] = 2; alert(a); // 1,2,3,4 alert(b); // 2,2,3,4 
// 对有多层属性的数组对象使用slice var a = [1,[1,2],3,4]; var b = a.slice(); b[1][0] = 2; alert(a); // 1,2,2,3,4 alert(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]);//5  console.log(a[0])//1  console.log(b[2][0]);//6  console.log(a[2][0])//6 

⑤es6的扩展运算符"..."

 var a=[1,2,[3,4]]  var b=[...a];  b[0]=5;  b[2][0]=6  console.log(b[0]);//5  console.log(a[0])//1  console.log(b[2][0]);//6  console.log(a[2][0])//6 

通过Object.create()实现

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)//1 console.log(obj2.a)//20 console.log(obj1.b.bc)//60 console.log(obj2.b.bc)//60 

2.粗暴深拷贝(抛弃对象的constructor)

使用JSON.stringify和JSON.parse实现深拷贝:JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象;

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,4 alert(b); // 2,2,2,3,4 

缺陷:它会抛弃对象的constructor,深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object;这种方法能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象,也就是说,只有可以转成JSON格式的对象才可以这样用,像function没办法转成JSON;

let obj1 = {    fun:function(){       alert(123);    } } let obj2 = JSON.parse(JSON.stringify(obj1)); console.log(typeof obj1.fun); // function console.log(typeof obj2.fun); // undefined 

3.复杂深拷贝(相对完美)

递归拷贝实现深拷贝,解决循环引用问题

/**  * 判断是否是基本数据类型  * @param value   */ function isPrimitive(value){   return (typeof value === 'string' ||    typeof value === 'number' ||    typeof value === 'symbol' ||   typeof value === 'boolean') }  /**  * 判断是否是一个js对象  * @param value   */ function isObject(value){   return Object.prototype.toString.call(value) === "[object Object]" }  /**  * 深拷贝一个值  * @param value   */ function cloneDeep(value){    // 记录被拷贝的值,避免循环引用的出现   let memo = {};    function baseClone(value){     let res;     // 如果是基本数据类型,则直接返回     if(isPrimitive(value)){       return value;     // 如果是引用数据类型,我们浅拷贝一个新值来代替原来的值     }else if(Array.isArray(value)){       res = [...value];     }else if(isObject(value)){       res = {...value};     }      // 检测我们浅拷贝的这个对象的属性值有没有是引用数据类型。如果是,则递归拷贝     Reflect.ownKeys(res).forEach(key=>{       if(typeof res[key] === "object" && res[key]!== null){         //此处我们用memo来记录已经被拷贝过的引用地址。以此来解决循环引用的问题         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) } 

二.ES插件lodash

import lodash from 'lodash'  var objects = [{ 'a': 1 }, { 'b': 2 }];  var deep = lodash.cloneDeep(objects); console.log(deep[0] === objects[0]); // => false 

站在巨人肩膀上摘苹果

https://www.jianshu.com/p/cf1e9d7e94fb

https://www.jianshu.com/p/5f6cd3dabc1c

https://segmentfault.com/a/1190000015455662

https://segmentfault.com/a/1190000018371840

https://www.jianshu.com/p/f4329eb1bace

https://www.lodashjs.com/docs/lodash