mis-lab / Front-End-Question-Discussion

前端基础问题 每日一题
1 stars 0 forks source link

JavaScript深浅拷贝以及实现方法 #2

Open Reaper622 opened 5 years ago

Reaper622 commented 5 years ago

JS 对象的深浅拷贝

深浅拷贝区别

浅拷贝的实现方法

 let obj = {
 name: 'Reaper',
 skill : {
    sing: "good",
    rap: "good",
    "basketball": "good"
 }
}
// 遍历赋值法
function shallowCopy(obj) {
    let newObj = {};
    for( let i in obj ) {
        newObj[i] = obj[i];
    }
    return newObj;
}
// Object assign 
let assignObj = Object.assign({}, obj);

let newObj = shallowCopy(obj);

console.log(newObj.name); //Reaper
newObj.skill.sing = "bad";
console.log(obj.skill.sing); // bad 产生了影响

深拷贝实现方法

 let obj = {
 name: 'Reaper',
 skill : {
    sing: "good",
    rap: "good",
    "basketball": "good"
 }
}
// 转义JSON方法,但存在一定的局限性,只能够处理那些能够转化为 json 的数据, 
//例如 RegExp 对象无法通过此种方法进行深拷贝
// 同时 值为 undefined function symbol 会在转换过程中被忽略
 function deepCopy(obj) {
     let newObj ={};
     newObj = JSON.parse(JSON.stringify(obj));
     return newObj;
 }
let newObj = deepCopy(obj);
newObj.skill.rap = "bad";
console.log(obj.skill.rap); // good
// 递归复制
function deepCopy2(obj) {
    let result = Array.isArray(obj) ? []: {}
    for(let i in obj) {
        if(obj.hasOwnProperty(i)) {
            // 若属性值为引用类型,递归遍历拷贝
            if ((typeof obj[i] === 'object') && (obj[i] !== null)) {
                result[i] = deepCopy2(obj[i]);
            } else {
                result[i] = obj[i];
            }
        }
    }
    return result;
}

let newObj2 = deepCopy2(obj);
newObj.skill.basketball = "bad";
console.log(obj.skill.rap); // good
Reaper622 commented 4 years ago

补充一个深拷贝没有考虑到的问题: 循环引用 即当一个对象内部引用它本身时,会存在超出最大执行栈的问题,此时我们可以使用一个Map来记录已经读取过的引用。

function deepCopy3(obj, map = new Map()) {
  if (map.get(obj)) {
     return obj;
  }
  let result = Array.isArray(obj) ? [] : {};
  map.set(obj, true)
  for(let i in obj) {
    if (obj.hasOwnProperty(obj[i])) {
      if ( typeof obj[i] === 'object' && obj[i] !== null) {
        result[i] = deepCopy3(obj[i], map)
      } else {
        result[i] = obj[i];
      }
    }
  }
  return result;
}

但此时又存在了另一个问题,那就是Map结构内的keyvalue是强引用,所谓强引用,即能够确保其引用的对象不会被垃圾回收。拿上面得例子来说,执行深拷贝后,mapobj就产生了强引用,此时obj的所占用的内存空间直到程序结束才会释放。

针对这个问题,ES本身也给我们提供了解决方案——WeakMap,整体操作与Map相同,只是内部为弱引用,它的键必须为对象,值可以为任意类型。此时我们可以对上面的深拷贝函数加以完善:

function deepCopy3(obj, map = new WeakMap()) {
  if (map.get(obj)) {
    return obj;
  }
  let result = Array.isArray(obj) ? [] : {};
  map.set(obj, true)
  for(let i in obj) {
    if (obj.hasOwnProperty(obj[i])) {
      if ( typeof obj[i] === 'object' && obj[i] !== null) {
        result[i] = deepCopy3(obj[i], map)
      } else {
        result[i] = obj[i];
      }
    }
  }
  return result;
}