Skip to content

深拷贝和浅拷贝

深拷贝和浅拷贝针对的其实都是我们的引用类型,对于基本类型其实只有赋值操作。

浅拷贝适用:适用于单层

深拷贝适用:多层

实现方式

浅拷贝的实现方式:

  1. Object.assign
  2. 扩展运算符
  3. Array.prototype.slice()
  4. Array.prototype.concat()
  5. Array.from()

深拷贝的实现方式:

  1. JSON.parse(JSON.stringify(obj))
  2. 递归
  3. lodash的cloneDeep方法

浅拷贝

浅拷贝的实现方式:

  1. Object.assign
  2. 扩展运算符
  3. Array.prototype.slice()
  4. Array.prototype.concat()
  5. Array.from()

浅拷贝方法都只会复制对象和数组的第一层属性,对于嵌套对象或数组的修改会影响到原对象

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

  • 简单举个例子: 我们现在想通过复制objA对象来创建一个objB对象,并且修改objB中的info属性,看看是否会影响objA对象。

js
 const objA = {
      name: 'zhangsan',
      age: 18,
      info: {
        gender: 'male',
        height: 180
      }
    }
    console.log(objA, 'objA--更改以前');
    const objB = objA;
    objB.age = 20;
    objB.info.gender = 'female';
    console.log(objB, 'objB');
    console.log(objA, 'objA--更改以后');

查看输出结果:

js
{
    "name": "zhangsan",
    "age": 20,
    "info": {
        "gender": "female",
        "height": 180
    }
},'objA--更改以前'


{ 
  name: 'zhangsan', 
  age: 20, 
  info: { gender: 'female', height: 180 } 
} 'objB'

{ 
  name: 'zhangsan', 
  age: 20, 
  info: { gender: 'female', height: 180 } 
} 'objA--更改以后'

查看输出我们可以发现,objB中的info属性被修改了,但是objA中的info属性也被修改了,这就是浅拷贝带来的问题。

基础类型中的age,浅拷贝成功了,但是引用类型中的info属性,浅拷贝并没有成功,因为浅拷贝只是拷贝了引用类型的地址,并没有拷贝地址指向的内容,所以修改objB中的info属性,objA中的info属性也被修改了。

1. Object.assign() 拷贝对象

js
const objA = {
    name: 'zhangsan',
    age: 18,
    info: {
      gender: 'male',
      height: 180
    }
  }
  const objB = Object.assign({}, objA);// 浅拷贝
  objB.info.gender = 'female';
  console.log(objB, 'objB');
  console.log(objA, 'objA--更改以后');

2. 展开运算符(...)

浅拷贝对象

对象的浅拷贝可以使用展开运算符(...)来实现,例如:

js
const original = { a: 1, b: { c: 2 } };
const shallowCopy = { ...original };

console.log(shallowCopy); // 输出: { a: 1, b: { c: 2 } }
shallowCopy.b.c = 3;
console.log(original.b.c); // 输出: 3 (原对象也受到影响)

浅拷贝数组

数组的浅拷贝可以使用展开运算符(...)来实现,例如:

js
const originalArray = [1, 2, [3, 4]];
const shallowCopyArray = [...originalArray];

console.log(shallowCopyArray); // 输出: [1, 2, [3, 4]]
shallowCopyArray[2][0] = 'changed';
console.log(originalArray); // 输出: [1, 2, ['changed', 4]]

3. Array.prototype.slice()

对于数组,可以使用 slice() 方法进行浅拷贝

js
const originalArray = [1, 2, [3, 4]];
const shallowCopyArray = originalArray.slice();

console.log(shallowCopyArray); // 输出: [1, 2, [3, 4]]
shallowCopyArray[2][0] = 'changed';
console.log(originalArray); // 输出: [1, 2, ['changed', 4]]

4. 使用 Array.from()

Array.from() 方法也可以用于创建数组的浅拷贝

js
const originalArray = [1, 2, [3, 4]];
const shallowCopyArray = Array.from(originalArray);

console.log(shallowCopyArray); // 输出: [1, 2, [3, 4]]
shallowCopyArray[2][0] = 'changed';
console.log(originalArray); // 输出: [1, 2, ['changed', 4]]

5. Array.prototype.concat() 拷贝数组

js
const arrA = [1, 2, 3, 4, 5];
const arrB = arrA.concat();// 浅拷贝

深拷贝

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

  • 深拷贝的实现方式:
  1. JSON.parse(JSON.stringify(obj))
  2. 递归
  3. lodash的cloneDeep方法

1、JSON.parse(JSON.stringify(obj))

JSON.parse(JSON.stringify(obj))是深拷贝最简单的一种方式,但是它有以下几个问题:

  • 会忽略undefined、symbol和函数
  • 不能序列化函数
  • 不能解决循环引用的对象
  • 不能正确处理new Date()和RegExp类型
  • 不能处理原型链上的属性

使用

js
const objA = {
  name: 'zhangsan',
  age: 18,
  info: {
    gender: 'male',
    height: 180
  },
  hobbyL:['篮球','足球']
}

const objB = JSON.parse(JSON.stringify(objA));

2、 递归

递归是深拷贝最常用的方法,但是需要注意循环引用的问题。

使用

  • 深拷贝的递归方式实现思路如下:

简单手写一个深拷贝函数,然后判断一下类型,如果是对象或者数组,则递归调用深拷贝函数,否则直接返回该值,这里注意的就是先判断数组类型,因为数组也是对象,但是我们需要先判断数组,否则会报错。

js
 // 深拷贝 
  const objA = {
    name: 'zhangsan',
    age: 18,
    info: {
      gender: 'male',
      height: 180
    },
    hobbyL:['篮球','足球']
  }

  const o=new Object();

  console.log(objA, 'objA--更改以前');
  function deepClone(newObj, oldObj) {  
    for (let k in oldObj) {
      let item = oldObj[k]; // 获取属性值
      if (item instanceof Array) {
        newObj[k] = []
        deepClone(newObj[k], item)
      } else if (item instanceof Object) {
        newObj[k] = {}
        deepClone(newObj[k], item)
      } else {
        newObj[k] = item
      }
    }
  }

3、lodash的cloneDeep方法

lodash是一个js工具库,封装了很多常用的方法,其中就包括深拷贝方法,我们可以直接使用。

官方的在线地址: https://www.lodashjs.com/

安装:

浏览器环境:

js
<script src="lodash.js"></script>

通过 npm:

js
$ npm i -g npm
$ npm i --save lodash

使用

js
const _ = require('lodash');
const objA = {
  name: 'zhangsan',
  age: 18,
  info: {
    gender: 'male',
    height: 180
  },
  hobbyL:['篮球','足球']
}
const objB = _.cloneDeep(objA);

Released under the MIT License.