深浅拷贝的进阶版

67 阅读2分钟

深浅拷贝

let obj = {

​ name: 'Tom',

​ age: 20,

​ msg: {

​ job: 'coder',

​ },

​ arr: [1,2,3],

​ fn: function(value) {

​ return value;

​ },

};

浅拷贝

方式一

const newObj1 = {}

for (let k in obj) {

​    newObj1[k] = obj[k]

}

console.log('方式一',newObj1,newObj1 === obj)

方式二

1、不会拷贝对象的继承属性

2、不会拷贝对象的不可枚举属性

3、可以拷贝symbol类型的属性

const newObj2 = {}

Object.assign(newObj2, obj)

console.log('方式二',newObj2,newObj2 === obj)

方式三

const newObj3 = { ...obj }

console.log('方式三', newObj3, newObj3 === obj)

深拷贝

方式一:乞丐版(JSON.stringfy)

  • 拷贝的对象值中如果有函数、undefiend、symbol这几种类型,经过JSON.stringfy序列化之后的字符串中这个键值对会消失

  • 拷贝Date引用类型会变成字符串

  • 无法拷贝不可枚举类型

  • 无法拷贝对象的原型链

  • 拷贝RegExp引用类型会变成空对象

  • 对象中含有NaN、infinity、以及-infinity、JSON序列化的结果会变成null

  • 无法拷贝对象的循环引用,即对象成环(obj[key] = obj)

方式二:基础版(手写递归实现)

  • 下列函数并不难复制不可枚举的属性以及Symbol类型
  • 这种只是针对普通的引用类型的值做递归复制
  • 对象的属性里面成环,即虚幻引用没有解决 RangeError: Maximum call stack size exceeded
function deepClone(target) {

  let newObj = target instanceof Array ? [] : {}

  for (let k in target) {
    
		if (typeof *target*[k] !== 'object') newObj[k] = *target*[k]
    
    else newObj[k] = deepClone(*target*[k])
  }

  return newObj

}

const deepObj = deepClone(obj)

console.log('deepObj',deepObj)

方式三:深拷贝的改进版

  • 针对能够遍历对象的不可枚举属性以及Symbol类型.,使用Reflect.ownKeys 方法
  • 利用Object的getOwnPropertyDescriptors方法可以获得对象的所有属性,以及对应的特性,再结婚Object的create 方法创建一个新对象.,并继承传入原对象的原型链
  • 利用WeakMap类型作为hash表,因为WeakMap是弱引用类型,可以有效防止内存泄露,作为检测循环引用很有帮助,如果存在循环,则直接返回WeakMap存储的值
const isComolexDataType = obj => (
   typeof obj === 'object' || typeof obj === 'function' 
) && (obj !== null)
const deepClone = function(obj, hash = new WeakMap()) {
  if (obj.constructor === Date) return new Date(obj) //日期对象直接返回一个新的日期对象
  if (obj.constructor === RegExp) return new RegExp(obj) // 正则对象直接返回一个新的正则对象
  //若是循环引用就用 weakMap 来解决
  if (hash.has(obj)) return hash.get(obj)
  let allDesc = Object.getOwnPropertyDescriptors(obj)
  // 遍历传入参数所有键的特性
  let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc)
  //继承原型链
  hash.set(obj, cloneObj)
  for (let key of Reflect.ownKeys(obj)) {
    cloneObj[key] = (isComolexDataType(obj[key]) && typeof obj[key] !== 'function') ? deepClone(obj[key],hash) : obj[key]
  }
  return cloneObj
} 
//测试代码
let testObj = {
  name: 'Tom',
  age: 22,
  boolean: true,
  unf: undefined,
  nu: null,
  obj: {
    name: 'Jakc',
    age:10000
  },
  arr: [1, 2, '34', { name: 'Herry' }],
  fn: function() {
    console.log('我是函数')
  },
  date: new Date(0),
  reg: new RegExp('/我是正则/ig'),
  [Symbol('1')]:1
}

Object.defineProperty(testObj, 'innumerable', {
  enumerable: false,
  value:'不可枚举属性'
})
testObj = Object.create(testObj, Object.getOwnPropertyDescriptors(testObj))
// 设置loop成循环引用的属性
testObj.loop = testObj
const testobj1 = deepClone(testObj)
testObj.arr.push(4873289)
console.log('完善', testobj1 === testObj);
console.log('testobj:',testObj)
console.log('testobj1',testobj1)