fedono / fe-questions

1 stars 0 forks source link

32. 深复制/浅复制 #33

Open fedono opened 3 years ago

fedono commented 3 years ago

浅拷贝: 仅仅是指向被复制的内存地址,如果原地址发生改变,那么浅复制出来的对象也会相应的改变。

创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。

深拷贝: 在计算机中开辟一块 新的内存地址 用于存放复制的对象。

将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象

深拷贝和浅拷贝最根本的区别在于是否是真正获取了一个对象的复制实体,而不是引用。

浅拷贝实现方法:

  1. Object.assign
    • Object.assign是一个浅拷贝,它只是在根属性(对象的第一层级)创建了一个新的对象,但是对于属性的值是仍是对象的话依然是浅拷贝
    • 不会拷贝对象继承的属性
    • 不可枚举的属性
    • 可以拷贝Symbol类型
  2. 扩展运算符、slice、concat
    • 和assgin一样只拷贝一层

深拷贝实现方法:

  1. 循环+递归
    • 只能实现object、array的深拷贝
    • for...in 无法获得 Symbol 类型的键,而 Reflect 可以获取
  2. JSON.stringify
    • 拷贝的对象的值中如果有函数,undefined,symbol则经过JSON.stringify()序列化后的JSON字符串中这个键值对会消失
    • 无法拷贝不可枚举的属性, 无法拷贝对象的原型链
    • 拷贝Date引用类型会变成字符串
    • 拷贝RegExp引用类型会变成空对象
    • 对象中含有NaN、 Infinity和 - Infinity, 则序列化的结果会变成null
    • 无法拷贝对象的循环应用(即obj[key] = obj)
fedono commented 3 years ago

深复制实现

主要考察的是递归、数组和对象的存储。

1. 使用 for ... in 的形式

function deepClone(obj) {
    // 注意点:深复制这里应该是[...obj] : {...obj} 而不应该是 [] : {} ,为什么呢?
    var result = Array.isArray(obj) ? [...obj] : {...obj} ;
    for (var key in obj) {
      if (obj.hasOwnProperty(key)) {
        if (typeof obj[key] === 'object' && obj[key]!==null) {
          result[key] = deepClone(obj[key]); 
        } else {
          result[key] = obj[key];
        }
      }
    }
    return result;
  }

2. 使用 Reflect.ownKeys 的方式

function deepClone(obj) {
  function isObject(o) {
    return (typeof o === 'object' || typeof o === 'function') && o !== null
  }

  if (!isObject(obj)) {
    throw new Error('非对象')
  }

  let isArray = Array.isArray(obj)
  let newObj = isArray ? [...obj] : { ...obj }
  Reflect.ownKeys(newObj).forEach(key => {
    newObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]
  })

  return newObj
}

使用 JSON.parse/JSON.stringify的形式

function deepClone(arr){
    return JSON.parse(JSON.stringify(arr))
}
fedono commented 3 years ago

对于上面说的

注意点:深复制这里应该是[...obj] : {...obj} 而不应该是 [] : {}

这一句还是没太明白,使用 [] : {} 来测试还是能够实现深复制啊

function deepClone(obj) {
  // 注意点:深复制这里应该是[...obj] : {...obj} 而不应该是 [] : {} ,为什么呢?
  let result = Array.isArray(obj) ? [] : {};
  // var result = Array.isArray(obj) ? [...obj] : {...obj} ;
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      if (typeof obj[key] === 'object' && obj[key] !== null) {
        result[key] = deepClone(obj[key]);
      } else {
        result[key] = obj[key];
      }
    }
  }
  return result;
}

let obj = {
  a: 1,
  b: {b1: 1, b2: 2},
  c: [1, 2],
  d: function() {}
};

let res = deepClone(obj);
console.log(res.b === obj.d) // false
console.log(res) // obj 的全部复制了