# 深浅拷贝

# 前言

  • 浅拷贝:复制原数据的基础类型的值,但遇到嵌套的引用类型时,由于直接赋值,拷贝后的对象某个 key 是引用类型时,双方用的是同一块堆内存,所以改变会互相影响。

  • 深拷贝:与浅拷贝区分不同,则就是拷贝后的对象即使 key 值是引用类型,也是新开辟一块内存空间存储,所以双方的改变不会互相影响。

  • 举个浅拷贝的例子:

const source = {
  a: 1,
  b: {
    c: 1
  }
};

// 用es6的解构实现浅拷贝
const target = { ...source };

target.b.c = 2;

console.log(source); // {a: b: {c: 2}} 会受target的改动影响。
reference-type

# 浅拷贝实现

# es6 解构

前言中的例子就是用的 es6 解构实现浅拷贝,可以想象一下,其实就是遍历对象的 key 值,然后做复制操作。

我们主要聚焦一下,使用这种方式实现之后有什么现象,会有什么存在的问题。

  • 不能复制原型链上的属性。
  • 不能复制不可枚举enumerable=false的属性。(对象的属性描述符
  • 可以复制 symbol 类型的属性。
let obj1 = { a: { b: 1 }, sym: Symbol(1) };

Object.defineProperty(obj1, 'innumerable', {
  value: '不可枚举属性',
  enumerable: false
});

let obj2 = { ...obj1 };

obj1.a.b = 2;

console.log('obj1', obj1);

console.log('obj2', obj2);
shallow-clone

# Object.assign

与 es6 解构实现效果一致,一些不支持 es6 解构语法的场景下可以使用。

let obj1 = { a: { b: 1 }, sym: Symbol(1) };

Object.defineProperty(obj1, 'innumerable', {
  value: '不可枚举属性',
  enumerable: false
});

let obj2 = Object.assign({}, obj1);

obj1.a.b = 2;

console.log('obj1', obj1);

console.log('obj2', obj2);

# 数组的两种浅拷贝方法

针对数组,也有两种特定的浅拷贝方式Array.proptotype.slice()Array.proptotype.concat()

const arr = [1, { a: 1 }, 3];

console.log(arr.slice());
console.log(arr.concat());

# 手动实现浅拷贝

遍历复制值,做一下数组和对象的区分即可。

const shallowClone = target => {
  if (typeof target === 'object' && target !== null) {
    const cloneTarget = Array.isArray(target) ? [] : {};

    for (let prop in target) {
      if (target.hasOwnProperty(prop)) {
        cloneTarget[prop] = target[prop];
      }
    }
    return cloneTarget;
  } else {
    return target;
  }
};

# 深拷贝实现

# 乞丐版 JSON.stringify JSON.parse

const deepClone = target => {
  try {
    return JSON.parse(JSON.stringify(target));
  } catch (err) {
    return {};
  }
};

这种方法是一种比较方便的浅拷贝方法,如果数据都是常见的字符串,数组,对象时,基本能满足日常需求。但是当值特殊时会存在一些问题:

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

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

  • 无法拷贝不可枚举的属性;

  • 无法拷贝对象的原型链

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

  • 对象中含有 NaNInfinity 以及 -Infinity,JSON 序列化的结果会变成 null

  • 无法拷贝对象的循环应用,即对象成环 (obj[key] = obj),会报错。

jsonparse-cycle

针对上述问题,下面代码是一个例子验证:


function Obj() {
  this.func = function() {
    alert(1);
  };

  this.obj = { a: 1 };

  this.arr = [1, 2, 3];

  this.und = undefined;

  this.reg = /123/;

  this.date = new Date(0);

  this.NaN = NaN;

  this.infinity = Infinity;

  this.sym = Symbol(1);
}

let obj1 = new Obj();

Object.defineProperty(obj1, 'innumerable', {
  enumerable: false,

  value: 'innumerable'
});

console.log('obj1', obj1);

let str = JSON.stringify(obj1);

let obj2 = JSON.parse(str);

console.log('obj2', obj2);
jsonparse

# 手动实现基础版

# 手动实现详细版

Last Updated: 6/25/2021, 6:35:25 AM