实现 js 中的深拷贝

1. 简单需求

var obj = {
  name: 'tom',
  age: 22
}
var p = JSON.parse(JSON.stringfiy(obj))

1.1. JSON的缺点:

  • 不支持函数
  • 不支持undefined(支持null)
  • 不支持循环引用, 比如: a = {name: ‘a’}; a.self = a; a2 = JSON.parse(JSON.stringify(a))
  • 不支持date, 会变成ISO8601字符串
  • 不支持正则表达式
  • 不支持Symbol
  • 如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
  • JSON.stringify()只能序列化对象的可枚举的自有属性

2. 递归拷贝

2.1. 基础版本,未考虑循环引用的问题

/**
 *
 * @param obj {Object}
 */

function deepClone(obj) {
  // 1.判断obj是null及undefined
  if (obj == null) return obj;
  // 2.判断不是对象就不用拷贝了
  if (typeof obj !== 'object') return obj;
  // 3.判断是Date就返回一个新的Date,因为日期也是对象
  if (obj instanceof Date) return new Date(obj);
  // 4.判断是正则,就返回一个新的正则,因为正则也是对象
  if (obj instanceof RegExp) return new RegExp(obj);
  // 5.不是数组就是对象
  // 注意:new obj.constructor 要不是[], 要不是{}
  let newObj = new obj.constructor;
  for (let key in obj) {
    // 如果不是原型链上的属性,再拷贝
    if (obj.hasOwnProperty(key)) {
      // 实现递归拷贝
      newObj[key] = deepClone(obj[key]);
    }
  }
  return newObj;
};
let obj = { age: 18, name: { firstName: 'jerry' } };
let result = deepClone(obj);
obj.age = 100;
console.log(result);

2.2. 升级版,利用WeakMap解决循环引用的问题

function deepClone(obj, hash=new WeakMap()) {
  // 1.判断obj是null及undefined
  if (obj == null) return obj;
  // 2.判断不是对象就不用拷贝了
  if (typeof obj !== 'object') return obj;
  // 3.判断是Date就返回一个新的Date,因为日期也是对象
  if (obj instanceof Date) return new Date(obj);
  // 4.判断是正则,就返回一个新的正则,因为正则也是对象
  if (obj instanceof RegExp) return new RegExp(obj);
  // 如果WeakMap中有对象就直接返回
  if (hash.has(obj)) return hash.get(obj);
  // 5.不是数组就是对象
  // 注意:new obj.constructor 要不是[], 要不是{}
  let newObj = new obj.constructor;
  // 如果是对象就放到hash里,再次拷贝这个对象就存在了,那么就直接返回这个对象
  hash.set(obj, newObj);
  for (let key in obj) {
    // 如果不是原型链上的属性,再拷贝
    if (obj.hasOwnProperty(key)) {
      // 实现递归拷贝
      newObj[key] = deepClone(obj[key], hash);
    }
  }
  return newObj;
};
let obj = { age: 18, name: { firstName: 'jerry' } };
let result = deepClone(obj);
obj.age = 100;
console.log(result);

2.3. 代码优化,最终版本

const isObject = (obj) => typeof obj === 'object' && obj !== null;

function deepClone(obj, hash = new WeakMap()) {
  if (!isObject(obj)) {
    return obj;
  }

  if (obj instanceof Date) {
    return new Date(obj);
  }

  if (obj instanceof RegExp) {
    return new RegExp(obj);
  }

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

  let newObj = new obj.constructor();
  hash.set(obj, newObj);

  if (Array.isArray(obj)) {
    obj.forEach((item, index) => {
      newObj[index] = deepClone(item, hash);
    });
  } else {
    Object.getOwnPropertyNames(obj).forEach((key) => {
      newObj[key] = deepClone(obj[key], hash);
    });
	
    Object.getOwnPropertySymbols(obj).forEach((key) => {
      newObj[key] = deepClone(obj[key], hash);
    });
  }

  return newObj;
}

let obj = { age: 18, name: { firstName: 'jerry' } };
// let obj = [{ age: 18, name: { firstName: 'jerry' } }];
let result = deepClone(obj);
obj.age = 100;
console.log(result);

下次再见

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

剑九 六千里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值