JS浅拷贝、深拷贝

64 阅读2分钟

1. 浅拷贝、深拷贝

  • 浅拷贝:创建一个新对象,这个对象有着原始对象属性值的一份拷贝。
    • 属性是基本类型 => 拷贝的就是基本类型的值
    • 属性是引用类型 => 拷贝的就是内存地址
      • 指向同一个对象
a = {value: 1}
b = {'foo': a}
c = {'foo': a}
// a的修改会同时影响b、c
a.value = 2;
console.log(b.foo.value); // 2
console.log(c.foo.value); // 2
  • 深拷贝:将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象
    • 属性是基本类型 => 拷贝的就是基本类型的值
    • 属性是引用类型 => 创建一个新对象,进行完整拷贝
      • 指向不同对象,修改互不影响

2. 浅拷贝方法

2.1 Object.assign()

将一个或者多个源对象中所有可枚举自有属性复制到目标对象,并返回修改后的目标对象。

s1 = {
    age: 10,
    info: {
        class: 9001
    }
}
s2 = Object.assign({}, s1) //{age: 10, info: {…}}
s1.info === s2.info // true

2.2 展开运算符 ...

s1 = {
    age: 10,

info: {
        class: 9001
    }
}
s2 = {...s1} //{age: 10, info: {…}}
s1.info === s2.info // true

2.3 Array.prototype.concat()

a1 = [1, '2', {class: 9001}]
a2 = a1.concat()
a2[2] === a1[2] // true

2.4 Array.prototype.slice()

a1 = [1, '2', {class: 9001}]
a2 = a1.slice()
a2[2] === a1[2] // true

3. 深拷贝方法

3.1 JSON.parse(JSON.stringify())

  • JSON.stringify将对象转成JSON字符串
  • JSON.parse把字符串解析成对象
s1 = {
    age: 10,
    info: {
        class: 9001
    }
}
s2 = JSON.parse(JSON.stringify(s1))
s1.info === s2.info // false

可以看到处理后 s2 的 info 为一个全新的对象

缺点

  • 函数丢失
  • undefined丢失
  • Date对象变成字符串
  • 无法解决循环引用
s3 = {
    age: undefined,
    getAge(){
        return age;
    },
    reg: /test/,
    date: new Date()
}
s4 = JSON.parse(JSON.stringify(s3))

image.png 通过上图的对比,可以发现 age \ getAge 丢失,reg变成空对象,date变成字符串。

3.2 手写深拷贝

要点:

  • 引用类型递归处理
  • 使用map处理循环引用
  • 函数
const deepClone = (a, map = new Map()) => {
  // 类型判断,引用类型
  if (a instanceof Object) {
    // 已缓存,则直接返回
    if (map.get(a)) {
      return map.get(a);
    }
    let result = {};
    if(a instanceof Array) {
      // 数组处理
      result = [];
    } else if (a instanceof Date) {
      // Date处理
      result = new Date(a);
    } else if (a instanceof RegExp) {
      // 正则处理,分为source、flags
      result = new RegExp(a.source, a.flags);
    }
    // 缓存
    map.set(a, result);
    // 遍历键
    for (let key in a) {
      // 只处理实例本身的属性
      if (a.hasOwnProperty(key)) {
        // 属性值需要递归处理
        result[key] = deepClone(a[key], map);
      }
    }
  } else {
    // 非引用类型直接返回
    return a;
  }
};