js 浅拷贝和深拷贝

182 阅读2分钟

区别

   引用类型的值拷贝还是引用拷贝的区别,也就是当 b 拷贝 a, 如果修改 b, a 也改变,则为浅拷贝,如果修改 b, a 不发生改变,则为深拷贝。

深拷贝实现

1. 只拷贝对象和数组的递归实现

function isObject(obj) {  return (typeof obj === 'object') && obj !== null; }function deepCopy(obj) {  if (!isObject(obj)) {    throw new Error('object 不是一个对象');  }  let isArray = Array.isArray(obj);  let cloneObj = isArray ? [] : {};  for(let key in obj) {    cloneObj[key] = isObject(obj[key]) ? deepCopy(obj[key]) : obj[key]  }  return cloneObj;}

    能拷贝undefined, 但是对于 function、Date、RegExp 和Error无法复制,因为它们有特殊的构造函数。

场景:

    1. 对象 key 值为 Symbol 时不能拷贝,因为 for in 无法获取 Symbol 类型的键,可以通过Object.getOwnPropertySymbols() 来获取 Symbol 类型的键。

支持拷贝 Symbol 类型的键:

function isObject(obj) {  return (typeof obj === 'object') && obj !== null; }function deepCopy(obj) {  if (!isObject(obj)) {    throw new Error('object 不是一个对象');  }  const isArray = Array.isArray(obj);  const cloneObj = isArray ? [] : {};  const symbolKeys = Object.getOwnPropertySymbols(obj) || [];  symbolKeys.forEach((symbolKey) => {    cloneObj[symbolKey] =  isObject(obj[symbolKey]) ? deepCopy(obj[symbolKey]) : obj[symbolKey];  })  for(let key in obj) {    cloneObj[key] = isObject(obj[key]) ? deepCopy(obj[key]) : obj[key];  }    return cloneObj;}

    2. 对象成环,obj1 添加一个属性,指向自身

const obj1 = {  a: 'test'}; obj1.g = obj1;

通过递归实现会出现栈溢出

通过序列化和反序列化也会报错:


如何处理对象成环

    lodash 使用的是栈把对象存储起来了,如果有环对象,就会从栈里检测到,从而直接返回结果,悬崖勒马。这种算法思想来源于 HTML5 规范定义的结构化克隆算法,它同时也解释了为什么 lodash 不对 Error 和 Function 类型进行拷贝。

当然,设置一个哈希表存储已拷贝过的对象同样可以达到同样的目的:

function isObject(obj) {  return (typeof obj === 'object') && obj !== null; }function deepCopy(obj, hash = new WeakMap()) {  if (!isObject(obj)) {    throw new Error('object 不是一个对象');  }   // 查表   if (hash.has(obj)) return hash.get(obj);  const isArray = Array.isArray(obj);  const cloneObj = isArray ? [] : {};  const symbolKeys = Object.getOwnPropertySymbols(obj) || [];  // 哈希表设值  hash.set(obj, cloneObj);  symbolKeys.forEach((symbolKey) => {    cloneObj[symbolKey] =  isObject(obj[symbolKey]) ? deepCopy(obj[symbolKey], hash) : obj[symbolKey];  })  for(let key in obj) {    cloneObj[key] = isObject(obj[key]) ? deepCopy(obj[key], hash) : obj[key];  }  return cloneObj;}

2. 序列化和反序列化

const cloneObj = JSON.parse(JSON.stringify(obj1))

不能拷贝 undefined,对象类型只能深拷贝对象和数组,对于其他种类的对象,会失真。