Akiq2016/blog

deep clone

Opened this issue · 0 comments

function getType(data) {
  let type = Object.prototype.toString.call(data).slice(8, -1).toLocaleLowerCase();

  if (type === 'object') {
    if (data instanceof Set) {
      type = 'set';
    } else if (data instanceof Map) {
      type = 'map';
    } else if (data instanceof Function) {
      type = 'function';
    }
  }

  return type;
}

function cloneArr(res, data, hash) {
  data.forEach((item) => {
    res.push(cloneDeep(item, hash));
  });
  return res;
}

function clonePlainObject(res, data, hash) {
  for (let key in data) {
    res[key] = cloneDeep(data[key], hash);
  }
  return res;
}

function cloneSet(res, data, hash) {
  Array.from(data, (val) => {
    res.add(cloneDeep(val, hash));
  })
  return res;
}

function cloneMap(res, data, hash) {
  Array.from(data, ([key, value]) => {
    res.set(cloneDeep(key, hash), cloneDeep(value, hash))
  })
  return res;
}

/**
 * @param {any} data
 */
function cloneDeep(data, hash = new WeakMap()) {
  let res;

  // handle circular scene
  if (hash.has(data)) {
    return hash.get(data);
  }

  if (typeof data === 'object') {
    res = new data.constructor();
    hash.set(data, res);
  }

  switch (getType(data)) {
    case 'array': res = cloneArr(res, data, hash); break;
    case 'object': res = clonePlainObject(res, data, hash); break;
    case 'set': res = cloneSet(res, data, hash); break;
    case 'map': res = cloneMap(res, data, hash); break;
    default: res = data; break;
  }

  return res;
}

let a = {
  a: 1,
  b: 2,
  c: {
    cc: 1,
    dd: [1, 2, 3],
    ee: {
      gg: '213'
    },
    hh: {
      ii: {
        name: 'jjjj',
      }
    }
  },
  i: new Set([1,2,2,3, { a: 1 }]),
  j: new Map([
    ['key', 'value'],
    [['key'], 'value'],
  ])
};
a.h = a;
a.c.hh.kk = a.c.hh;

let b = cloneDeep(a);
console.log(a)
console.log('----')
console.log(b);
console.log(a.a === b.a === true)
console.log(a.b === b.b === true)
console.log(a.c === b.c === false)
console.log(a.c.cc === b.c.cc === true)
console.log(a.c.dd === b.c.dd === false)
console.log(a.c.ee === b.c.ee === false)
console.log(a.c.ee.gg === b.c.ee.gg === true)
console.log(a.i === b.i === false)
console.log(a.j === b.j === false)
console.log(a.h === a, b.h === b )