JS中深拷贝的实现
- JSON.parse(JSON.stringify())
- 递归实现深拷贝
使用
JSON.parse(JSON.stringify())实现 无法拷贝 函数、正则、时间格式、原型上的属性和方法等
递归实现深拷贝
es5实现深拷贝
- 源对象
const obj = {
    name: '张桑',
    age: 18,
    hobby: [
      {
        name: '篮球',
        year: 5,
        loveStar: ['Luka', 'Curry', 'Dragic']
      }
    ],
    run: function () {
      return 'xxxx'
    }
  }
- deepClone实现
// 深拷贝(es5实现)
function deepClone (origin, target) {
  // 1.判断传入的origin 是否存在
  var tar = target || {}
  // 5.toString是 object对象原型上的方法 Object.prototype.toString.call({}) => [object Object]
  var toStr = Object.prototype.toString
  var arryType = '[obeject Array]'
  // 2.遍历对象
  for (var k in origin) {
    // 4.这里不能拷贝其原型上的属性
    if (origin.hasOwnProperty(k)) {
       // 3.判断k是否为对象 且不能为null  typeof null === 'object'
      if (typeof origin[k] === 'object' && origin[k] !== null) {
        // 6. 这里已经判断origin[k] 是object 只需要判断是{} 还是 []
        tar[k] = toStr.call(origin[k]) === arryType ? [] : {}
        deepClone(origin[k], tar[k])
      } else {
        tar[k] = origin[k]
      }
    }
  }
  return tar
}
const newObj = deepClone(obj, {})
obj.age = 19
console.log(newObj)
实现思路
 1. 先判断传入的origin是否存在
 2. 存在就遍历该对象
 3. 通过hasOwnProperty(k)判断是否是其原型上的属性,不拷贝其原型上的属性
 4. 判断k是否为对象 且不能为null typeof null === 'object'
 5. 通过是 object对象原型上的方法 toString 判断k是{} 还是[]
Object.prototype.toString.call({})  // '[object Object]'
Object.prototype.toString.call([])  // '[object Array]'
6.再进行递归拷贝deepClone(origin[k], tar[k])
es6实现深拷贝
// null undefined 和 不是对象的数据 直接返回  
// 封装一个判断方法
function isObject(origin) {
  return typeof origin === "object" && origin !== null;
}
function deepClone (origin) {
  if (!isObject(origin)) return origin
  // 还需要考虑的 Date RegExp 等 可以通过instanceof 判断其构造函数
  if (origin instanceof Date) {
    return new Date(origin)
  }
  if (origin instanceof RegExp) {
    return new RegExp(origin)
  }
  // 处理 {} []
  // 通过构造器判断它是否为对象、 数组
  /**
   * const obj = {}
   * const newObj = new obj.constructor() // 相当于拷贝了origin
   * 这样就不需要判断origin是对象还是数组
  */
  const target = new origin.constructor()
  for (let k in origin) {
    if (origin.hasOwnProperty(k)) {
      target[k] = deepClone(origin[k])
    }
  }
  return target
}
const newObj = deepClone(obj)
console.log(newObj)
let target = {name: 'target'}; 
target.target = target
const newObj = deepClone(target)
console.log(newObj)
实现思路
- 这里首先封装了一个方法用来 null、 undefined 和 不是对象的数据,这些数据直接可以返回
	function isObject(origin) {
	  return typeof origin === "object" && origin !== null;
	}
	// deepClone中判断
	if (!isObject(origin)) return origin
- 针对Date、RegExp等 可以通过instanceof判断其构造函数来区分,做特殊处理。
	 if (origin instanceof Date) {
	    return new Date(origin)
	  }
	  if (origin instanceof RegExp) {
	    return new RegExp(origin)
	  }
- es5方法中是通过是 object对象原型上的方法 toString判断k是{}还是[],这里可以使用new 其对象的构造方法则不需要判断origin是对象还是数组
 const target = new origin.constructor() // 相当于拷贝了origin
- 遍历origin,递归拷贝origin[k]
使用WeakMap优化
问题
- 循环拷贝(对象的属性引用自己) 问题
 使用上面es6 的deepClone方法,直接栈溢出了
let target = {name: 'target'}; 
target.target = target
const newObj = deepClone(target)
console.log(newObj)

 2. 重复拷贝(对象的属性引用同一个对象) 问题
let obj = {}; 
let target = {a: obj, b: obj};
这里拷拷贝a的时候拷贝了obj,拷贝b的时候又拷贝了一次
// 深拷贝 (WeakMap)
// 1. 解决循环拷贝(对象的属性引用自己)  问题
// 2. 解决重复拷贝(对象的属性引用同一个对象) 问题
// WeakMap 防止内存泄漏  键名如果外部没有引用,键值键名就会被内存回收掉
function isObject(origin) {
  return typeof origin === "object" && origin !== null;
}
function deepClone (origin, hashMap = new WeakMap()) {
  if (!isObject(origin)) return origin
  // 还需要考虑的 Date RegExp 等 可以通过instanceof 判断其构造函数
  if (origin instanceof Date) {
    return new Date(origin)
  }
  if (origin instanceof RegExp) {
    return new RegExp(origin)
  }
  const hashKey = hashMap.get(origin)
  if (hashKey) {
    return hashKey
  }
  // 处理 {} []
  // 通过构造器判断它是否为对象、 数组
  /**
   * const obj = {}
   * const newObj = new obj.constructor() // 相当于拷贝了obj
   * 这样就不需要判断origin是对象还是数组
  */
  const target = new origin.constructor()
  hashMap.set(origin,target)
  for (let k in origin) {
    if (origin.hasOwnProperty(k)) {
      target[k] = deepClone(origin[k], hashMap)
    }
  }
  return target
}
let target = {name: 'target'}; 
target.target = target
const newObj = deepClone(obj)
console.log(newObj)
实现思路
 这里给deepClone 方法加了一个参数,默认是new WeakMap(),利用WeakMap键名如果外部没有引用,键值键名就会被内存回收掉的特性,记录origin是否被拷贝过,拷贝过则直接返回
  const hashKey = hashMap.get(origin) // 查询是否有origin这个键
  if (hashKey) {
    return hashKey
  }
  
  hashMap.set(origin,target) // 拷贝前将其存起来



















