深克隆

137 阅读2分钟

版本1

const b = JSON.parse(JSON.stringify(b))

缺点:

  • 不支持函数
  • 不支持循环引用
  • 不支持正则表达式
  • 不支持new Date()
  • 不支持Symbol()
  • 不支持undefined

版本二

克隆

  • 函数
  • 日期
  • 正则
  • 普通对象
  • 数组

克隆函数

function cloneFn(fn) {
  if (fn.prototype) {
    return function () {
      return fn.call(this, ...arguments);
    };
  } else {
    return (...args) => fn.call(undefined, ...args);
  }
}

克隆日期

const a = new Date(1997)
console.log(a - 0); // 1997
const b = new Date(a - 0);

克隆正则

const a = /hi/
const b = new RegExp(a.source, a.flags);

克隆object

export function isObject(value) {
  return typeof value === 'object' && value !== null;
}

export function deepClone(data) {
  if (isObject(data)===false) return data;

  let res = {};
  
  for (let key in data) {
    res[key] = deepClone(data[key]);
  }
  return res;
}

克隆数组

export function isFunction(value) {
  return typeof value === 'function';
}

export function deepClone(data) {
  if (isFunction(data) === false) return data;

  let res = []
  for (let key in data) {
    res[key] = deepClone(data[key]);
  }
  return res;
}

不克隆继承来的属性

const person = {
  name: 'Bob',
  age: 30
};

// 继承属性
person.__proto__.country = 'USA';

for (const key in person) {
  if (person.hasOwnProperty(key)) {
    console.log(key); // 只输出: 'name', 'age'
  }
}
export function isObject(value) {
  return typeof value === 'object' && value !== null;
}

export function deepClone(data) {
  if (isObject(data)===false) return data;

  let res = {};
  
  for (let key in data) {
      if (data.hasOwnProperty(key)) res[key] = deepClone(data[key]);
  }
  return res;
}

ES6代替方案

// Object.keys() 只返回自身属性
Object.keys(obj).includes('name'); // true

// Object.getOwnPropertyNames()
Object.getOwnPropertyNames(obj).includes('name'); // true

// Reflect.ownKeys()(包含Symbol)
Reflect.ownKeys(obj).includes('name'); // true

解决无限循环

export function isObject(value) {
  return typeof value === 'object' && value !== null;
}

export function deepClone(data, hash = new Map()) {
  if (isObject(data) === false) return data;
  if (hash.get(data)) return hash.get(data);

  let res = {};

  hash.set(data, res);
  for (let key in data) {
    res[key] = deepClone(data[key], hash)
  }
  return res;
}

封装

export function isObject(value) {
  return typeof value === 'object' && value !== null;
}

function cloneFn(fn) {
  if (fn.prototype) {
    return function () {
      return fn.call(this, ...arguments);
    };
  }
  return (...args) => fn.call(undefined, ...args);
}

export function deepClone(data, hash = new Map()) {
  if (isObject(data) === false) return data;
  if (hash.get(data)) return hash.get(data);

  let res = {};
  if (data instanceof Array) res = [];
  if (data instanceof Date) res = new Date(data - 0);
  if (data instanceof RegExp) res = new RegExp(data.source, data.flags);
  if (data instanceof Function) res = cloneFn(data);

  hash.set(data, res);
  for (let key in data) {
    if (data.hasOwnProperty(key)) res[key] = deepClone(data[key], hash); // 不克隆继承来的属性
  }
  return res;
}

版本三

克隆

  • 普通对象
  • 数组
  • Map
  • Set
function deepClone(data: any, hash = new WeakMap()) {
  if (!isObject(data)) {
    return data;
  }

  if (hash.has(data)) {
    return hash.get(data);
  }

  let res: any = {};
  hash.set(data, res);

  if (data instanceof Array) {
    res = data.map((item) => deepClone(item, hash));
  }
  if (data instanceof Map) {
    res = new Map();
    data.forEach((v, k) => {
      const v1 = deepClone(v, hash);
      const k1 = deepClone(k, hash);
      res.set(k1, v1);
    });
  }
  if (data instanceof Set) {
    res = new Set();
    data.forEach((item) => {
      const item1 = deepClone(item, hash);
      res.add(item1);
    });
  }

  Object.keys(data).forEach((key) => {
    res[key] = deepClone(data[key], hash);
  });

  return res;
}

function isObject(data: any) {
  if (data === null) return false;
  if (typeof data === "object") return true;
  return false;
}

测试

值类型

console.log(deepClone(100));
console.log(deepClone("abc"));
console.log(deepClone(null));

普通对象和数组


const obj = {
  name: "jack",
  info: {
    city: "北京",
  },
  arr: [1, 2, 3],
};

const obj2 = deepClone(obj);
obj.info.city = "上海";
console.log(obj.info.city, obj2.info.city); // 上海, 北京

obj.arr.push(5);
console.log(obj.arr, obj2.arr); // [1,2,3,5] [1,2,3]

Map

const m1 = new Map();
m1.set("x", 10);
m1.set("y", 20);
const m2 = deepClone(m1);
console.log(m2.size); // 2

const obj1 = {
  map: m1,
};
const obj2 = deepClone(obj1);
console.log(obj2.map.size); // 2

Set


const set1 = new Set([1, 2, 3]);
const set2 = deepClone(set1);
console.log(set2.size); // 3

const obj1 = {
  set: set1,
};
const obj2 = deepClone(obj1);
console.log(obj2.set.size); // 3

循环引用

const a: any = {};
a.self = a;

const b = deepClone(a);
console.log(b.self === b, b.seft === a); // true, false