深拷贝和浅拷贝

136 阅读3分钟

思路

  1. 什么是拷贝或者说复制
  2. 有哪些数据类型,对于不同数据类型来说复制或者说拷贝都是什么操作和意义
  3. 什么是深拷贝或者说浅拷贝
  4. 如何去实现一个深拷贝
  5. 如何实现引用类型里面仍然有引用对象的情况?

1. 什么是拷贝

拷贝,是英文copy的音译词,意为复制。

2. EcmaScript变量

(1)类型

  • 基本类型:undefined、null、Boolean、String、Number、symbol、BigInt
  • 引用类型:Object-> 细分为Object(对象,{})、Array(数组,[])、Date、Function等

(2)存储和拷贝方式

  • 基本类型(简单类型):名(name)和值(value)都存储在栈内存中,所以拷贝时会在栈内存中开辟一个新的内存,两者互相独立。
  • 引用类型(复杂类型):名存于栈内存中,真实的值存于堆内存中。其中,栈内存的值存储的是指向堆内存中对应值的引用地址即指针。所以拷贝时复制的是对应值的引用地址,而非堆内存里的真实值,两者的值实际指向的是同一个地址。
// 基本类型
let a = 1;
let b = a;
b = 2;
=>   a = ?   2(❌) 1(✅)
// 引用类型
let a = { obj1: 1, obj2: 2 };
let b = a;
b.obj1 = 3;
=> a.obj1 = ?  1(❌) 3(✅)

可知,拷贝是得到一个新的完全一样的name-value对。只是对基本类型而言,值虽然相同但彼此独立存储互不干扰,而对引用类型而言,存储的value虽然在不同的栈内存,但都指向同一地址的数据。 举例:将一个苹果a拷贝得到另一个苹果b,吃了a之后b仍然存在,这是基本类型的拷贝;给a同学取别名b,a和b都指的是这位同学,那么该同学做任何改变都影响a和b,这是引用类型的拷贝。

3. 什么是深拷贝

深拷贝仅针对引用类型。浅拷贝只是增加了一个指针指向已存在的内存地址,仅仅是指向被复制的内存地址,如果原地址发生改变,那么浅复制出来的对象也会相应的改变。深拷贝是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存。

4. 如何实现一个深拷贝

(1)扩展运算符...

ES6新增方法,可用于数组或对象。 具体用法为 被赋值 = ...赋值,

const a = [1, 2, 3];
const b = { test: '测试' };
const a1 = [...a];
const b1 = {...b};

a.push(2);
b.test = '是否改变';

console.log(a, a1, b, b1);
// [1, 2, 3, 4], [1, 2, 3], { test: '是否改变' }, { test: '测试' }

(2)Object.assign()

用于将所有可枚举属性的值从一个或多个源对象分配到目标对象,将返回目标对象,仅可用于对象。 具体用法为 Object.assign(target,...sources), target为目标对象,sources为源对象,即Object.assign(目标对象, 源对象1, 源对象2, 源对象3……)。 其中,若属性重复,则会按照目标对象-源对象顺序覆盖。

const a = [1, 2, 3];
const b = { test: '测试' };
const a1 = Object.assign(;
const b1 = {...b};

a.push(2);
b.test = '是否改变';

console.log(a, a1, b, b1);
// [1, 2, 3, 4], [1, 2, 3], { test: '是否改变' }, { test: '测试' }

(3)手写实现:遍历

const deepCopy = (obj) => {
  const target = {};
  Object.keys(obj).map((prop) => {
    target[prop] = obj[prop];
  });
  return target;
}

4. 如何实现引用类型里面仍然有引用对象的情况?

(1)JSON.stringify & JSON.parse

JSON.stringify 方法将一个JavaScript对象或值转为JSON字符串,对应的JSON.parse用来解析JSON字符串,构造由字符串描述的JavaScript值或对象。 json深拷贝注意事项 惊艳面试官的深拷贝

(2)手写实现

const deepCopy = (source) => {
  const target = Array.isArray(source) ? [] : {};
  Object.keys(source).map((prop) => {
    if(typeof prop === 'object') {
      // 这里需要确认一下属性是否可以为null,因为null的typeof结果返回也是object
      deepCopy(prop);
    } else {
      target[prop] = source[prop];
    }
  });
  return target;
}