JS中的深拷贝和浅拷贝

Posted Nayek

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JS中的深拷贝和浅拷贝相关的知识,希望对你有一定的参考价值。

浅拷贝

浅拷贝是拷贝第一层的拷贝

使用Object.assign解决这个问题。

let a = {
  age: 1
}
let b = Object.assign({}, a)
a.age = 2
console.log(b.age) // 1

通过展开运算符 ... 来实现浅拷贝

let a = {
  age: 1
}
let b = {...a};
a.age = 2;
console.log(b.age)  // 1

深拷贝

简单的做法:JSON.parse(JSON.stringfy(obj))
但是该方法也是有局限性的:

  • 会忽略undefined
  • 会忽略symbol
  • 会忽略函数
  • 不能解决循环引用的对象 (会抱错)

如果你所需拷贝的对象含有内置类型并且不包含函数,可以使用 MessageChannel

自封装深拷贝
思路:

  1. 使用for-in遍历对象
  2. 因为for-in会遍历原型链上的属性,所以需要判断属性是否在原型链上,不是原型链才拷贝
  3. 判断属性值类型是原始类型和引用类型
  4. 原始类型直接赋值(注意null)
  5. 引用类型判断是对象还是数组,创建对应的空对象或空数组,递归调用函数,将值赋值进去
/**
 * 深度克隆
 * @param   origin 被拷贝的原对象
 * @param   target 拷贝出来的对象
 * @return         拷贝出来的对象
 */
function deepClone(origin, target) {
  if(typeof(origin) !== 'object' || origin) {
    return origin;
  }
  target = target || {};

  for(let prop in origin) {   //使用 for-in
    if(origin.hasOwnProperty(prop)) { //不是原型链上的
      if(typeof(origin[prop]) === 'object' && origin[prop] ) { //是对象
        // 先判断是不是数组
        if(origin[prop] instanceof Array) {
          target[prop] = [];
          deepClone(origin[prop], target[prop]);
        } else {
          target[prop] = {};
          deepClone(origin[prop], target[prop]);
        }
      } 
      else {
        target[prop] = origin[prop];
      }
    }
  }
  return target;
}

深拷贝 - BFS

// 如果是对象/数组,返回一个空的对象/数组,
// 都不是的话直接返回原对象
function getEmptyArrOrObj(item) {
  let itemType = Object.prototype.toString.call(item) 
  if(itemType === '[object Array]') {
    return []
  }
  if(itemType === '[object Object]') {
    return {}
  }
  return item
}

function deepCopyBFS(origin) {
  const queue = []
  const map = new Map() // 记录出现过的对象,处理环

  let target = getEmptyArrOrObj(origin)

  if(target !== origin) {
    // 说明origin是一个对象或数组,需要拷贝子代
    queue.push([origin, target]);
    map.set(origin, target)
  }

  while(queue.length) {

    let [ori, tar] = queue.shift(); // 出队

    for(let key in ori) {
      if(ori.hasOwnProperty(key)) { // 不在原型上

        if(map.get(ori[key])) { // 处理环
          tar[key] = map.get(ori[key])
          continue
        }

        tar[key] = getEmptyArrOrObj(ori[key]);
        if(tar[key] !== ori[key]) {
          queue.push(ori[key], tar[key])
          map.set(ori[key], tar[key])
        }
      }
    }
  }

  return target
}

深拷贝 - DFS

function deepCopyDFS(origin){
    let stack = [];
    let map = new Map(); // 记录出现过的对象,用于处理环

    let target = getEmptyArrOrObj(origin);
    if(target !== origin){
        stack.push([origin, target]);
        map.set(origin, target);
    }

    while(stack.length){
        let [ori, tar] = stack.pop();
        for(let key in ori){
      if(ori.hasOwnProperty(key)) { // 不在原型上
        // 处理环状
        if(map.get(ori[key])){
          tar[key] = map.get(ori[key]);
          continue;
        }

        tar[key] = getEmptyArrOrObj(ori[key]);
        if(tar[key] !== ori[key]){
          stack.push([ori[key], tar[key]]);
          map.set(ori[key], tar[key]);
        }
      }
        }
    }

    return target;
}

测试上面的两个 deepCopy

以上是关于JS中的深拷贝和浅拷贝的主要内容,如果未能解决你的问题,请参考以下文章

解析js中的深拷贝和浅拷贝

解析js中的深拷贝和浅拷贝

js 中的深拷贝和浅拷贝

浅谈js中的深拷贝和浅拷贝

jquery怎样深拷贝一个数组

JS 对象的深拷贝和浅拷贝