什么是深拷贝/深复制?什么是浅拷贝/浅复制?本文将针对深浅拷贝这道常见面试题呈现深浅拷贝的理解,以及深浅拷贝的方法归类。将呈现多种深浅拷贝的方法,包括手写递归深拷贝!

image.png

什么是浅拷贝:

浅拷贝(Shallow copy)顾名思义,就是浅层次的拷贝,有多浅呢?浅到只能拷贝一层。浅拷贝是指将某个对象拷贝/复制成一个新的对象,使新的对象具有和被拷贝的对象相同的属性、属性值和方法。但是这种拷贝只拷贝第一层,因为无论是对象还是数组,都能在里面再嵌套数组和对象,都能无限嵌套。而对象/数组都是属于引用数据类型,堆中存储的是引用地址,栈中才存储的是对应数据。

以上描述可能晦涩难懂,更简单的描述如下:

浅拷贝只拷贝对象或者数组的第一层数据,基本数据类型拷贝属性和属性值,引用数据类型拷贝引用地址

因为浅拷贝无法拷贝嵌套的数组或者对象,因此深拷贝应运而出,将由深拷贝解决这个问题!

下面是大C老师的回答:

image.png

浅拷贝的方法:

对象浅拷贝有两种常用的方法:

扩展运算符(展开运算符)

const originalObj = { name: "John", age: 25 };
const shallowCopyObj = { ...originalObj };

Object.assign()

const originalObj = { name: "John", age: 25 };
const shallowCopyObj = Object.assign({}, originalObj);

数组常用的浅拷贝方法:

扩展运算符(展开运算符)

const originalObj = { name: "John", age: 25 };
const shallowCopyObj = { ...originalObj };//和这对象的的浅拷贝方法一致

Array.from()方法

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

Array.prototype.slice()方法

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

Array.concat()方法

const newArray = array1.concat(array2, array3, ..., arrayN);

什么是深拷贝:

深拷贝(Deep copy)简单的说是将对象全部拷贝,如果有嵌套,则每层都拷贝,并非只拷贝地址,而是拷贝完整的数据,使两个对象之间无论嵌套了多少层对象,都不会互相干扰!

深拷贝:

在堆内存中开辟一个空间,创建一个新对象

递归的拷贝原对象的所有属性和方法,拷贝前后两个对象,相互不影响

下面是大C老师的原话:

image.png

深拷贝的方法:

一般来说,深拷贝有三种常用的方式:

1、手写递归深拷贝

这个将放到下面去详解,因为代码量比较多!

2、序列化搭配反序列化

这个就是使用JSON.stringify()JSON.parse()方法,但是这样也会有一定的缺陷

缺陷:

①拷贝对象的属性值,如果是(function/undefined)/ symbol,这个键值对丢失

②如果深拷贝对象的属性值是RegExp,会变成空对象{}

③如果是NaN,Infinity,属性值会变成null

④如果拷贝日期对象,会变成日期字符串

3、第三方lodash插件库的深拷贝

// 引入js
<script src="./js/lodash.min.js"></script>

const obj = {
    name: 'zjl',
    age: 18,
    hobby:['dance','music'],
    book:{
        book_name:'宗波尘客',
        price:66
    }, 
    test0:function(){},
    test1:undefined,
    test2:Symbol('id'),
    test3:new RegExp(/abc/, 'i'),
    test4:NaN,
    test5:Infinity,
    test6:new Date()  
}
// 深拷贝  _是lodash的一个对象,里面有一个方法 cloneDeep()
const o = _.cloneDeep(obj)
console.log(o)


手写递归深拷贝详解:

将用有一定区别的几种手写递归深拷贝的方式用代码呈现给大家,大家根据自己最容易看懂的去理解,然后能手写出来即可!

代码1:

const obj = {
    name: 'zjl',
    age: 18,
    hobby:['dance','music'],
    book:{
        book_name:'Golang',
        price:66
    }
}

// 递归实现深拷
function deepClone(obj){
    // 1. 如果是基本数据类型,直接返回
    if (typeof obj !== 'object') return obj
    // 2. 判断传入的是数组还是对象,在内存中新创建数组/对象
    let newObj = Array.isArray(obj)? [] : {}

    // 给新创建的对象添加属性和值,遍历传入过来的那个对象的属性,一个一个添加到新对象上

    return newObj
}

const res = deepClone()
console.log(res)

代码2:

const obj = {
      name: 'zjl',
      age: 18,
      hobby:['dance','music'],
      book:{
          book_name:'Golang',
          price:66
      }
  }

// 深拷贝,在内存中开辟一个空间,创建一个新的对象, ===> 考虑数组和对象两种情况

// 递归实现深拷
function deepClone(obj){
  // 1. 如果是基本数据类型,直接返回
  if (typeof obj !== 'object') return obj
  // 2. 判断传入的是数组还是对象,在内存中新创建数组/对象
  let newObj = Array.isArray(obj) ? [] : {}

  // 给新创建的对象添加属性和值,遍历传入过来的那个对象的属性,一个一个添加到新对象上
  for(let key in obj){
      if (typeof obj[key] === 'object'){
          // 当key 取 hobby时, obj[key] 是 ['dance','music'] 把它传入到deepClone再拷贝
          newObj[key] = deepClone(obj[key])
      } else {
          newObj[key] = obj[key]
      }
  }
  return newObj
}

const res = deepClone(obj)
console.log(res)

res.hobby[0] = 'rapper'
console.log(obj)

代码3:

function deepCopy(obj) {
  if (typeof obj !== "object" || obj === null) {
    return obj; // 基本数据类型和 null 直接返回
  }

  var clone = Array.isArray(obj) ? [] : {};

  for (var key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      clone[key] = deepCopy(obj[key]); // 递归复制每个属性的值
    }
  }

  return clone;
}

// 示例对象
var originalObj = {
  name: "John",
  age: 25,
  hobbies: ["reading", "running"],
  address: {
    street: "123 Main St",
    city: "New York"
  }
};

// 深拷贝对象
var deepCopyObj = deepCopy(originalObj);

// 修改深拷贝对象的属性
deepCopyObj.name = "Jane";
deepCopyObj.hobbies.push("painting");
deepCopyObj.address.city = "Los Angeles";

console.log(originalObj.name); // 输出: "John"
console.log(originalObj.hobbies); // 输出: ["reading", "running"]
console.log(originalObj.address.city); // 输出: "New York"

代码4:

image.png

关于深浅拷贝提示:

这是一道常见面试题,一般面试官会询问什么是深浅拷贝,深浅拷贝的区别,以及可能会让你手写深浅拷贝。本文仅包含常用的深浅拷贝方法,如需要更多方法,可咨询大C老师哦!