浅拷贝与深拷贝

55 阅读3分钟

说道深拷贝, 浅拷贝, 我们得从js的数据类型开始讲起.

一. js的数据类型分为基础数据类型 和 引用数据类型

    1. 基础数据类型: string, Namber, Boolean, undifind, null
    2. 引用数据类型: Array, Object, Function... 等        
    
   两种数据类型的区别在于, 储存的方式不一样, 基础数据类型是直接将数据存放在栈中;
而引用数据类型是将地址存放在栈中,而真实数据存放在堆中, 栈中的地址指向堆中的数据.
 

变量在做赋值运算时, 都是将栈中的数据给等号右边, 所以两种数据在做赋值运算时会出现下面情况:

// 基础数据
let a = 1
let b = a
b = 2
console.log(a, b) // 1, 2 不会出现什么特殊情况

// 引用数据类型
let obj1 = {a: 1, b: 2}
let obj2 = obj1
obj2.a = 3
console.log(obj1) // {a: 3, b: 2} 

是不是很奇怪, 我们改变obj2, obj1为什么也改变了; 是因为obj1是一个对象, 真实数据是存放在堆中, 而 let obj2 = obj1 赋值时是将obj1栈中的地址给了obj2 他们栈中的地址是一样的, 所以会指向同一个堆中的数据, 这就是为什么改变obj2中的属性, obj1 也会一起变化

Snipaste_2022-06-11_22-32-01.png

那么问题就来了, 如果要将引用数据类型的变量赋给另一个变量, 并且让两个变量完全独立, 相互不影响, 我们可以用如下方法

    //1. 最简单方法
    let obj1 = {a: 1, b: 2}
    let obj2 = {...obj1}
    obj2.a = 3
    console.log(obj1) // {a: 1, b: 2}
    console.log(obj2) // {a: 3, b: 2}
    // 两个变量就独立了

上述方法: 可以实现将两个变量独立起来, 但是也只能拷贝一层数据,也就是浅拷贝 不过日常开发使用的也最多;

现在如果数据有多层, 也就是对象里面嵌套着对象

let obj1 = {a: {aa: 1, ab: 2}, b: {ba: 2, bb: 4}}
let obj2 = {...obj1}
obj2.a.aa = 5
obj1.a.aa = 5 // 还是5

这里如果想要将两个对象完全独立, 就要涉及到深拷贝了

深拷贝方案有两种

1 JSON 
let obj1 = {a: {aa: 1, ab: 2}, b: {ba: 2, bb: 4}}
let obj2 = JSON.parse(JSON.stringify(obj1))

这样两个对象就完全独立了,但是此方法还是存在一个问题 就是如果 obj1 中如果有方法 就不能这么用了, 函数拷贝不过来; obj2最后只能是一个空对象

let obj1 = {a: function(){console.log('aaa')}}
let obj2 = JSON.parse(JSON.stringify(obj1))

要解决上面的问题就要用到最令人头疼的递归了,

var obj = {
      name: '小王',
      hobby: ['reading', 'running']
    }

    // 对于当前要拷贝的内容,如果还是一个对象,则要递归调用,而不是直接赋值
    function deepCopy(obj) {
      // 定义一个空的对象或者是数组
      // 如果当前要复制的是数组就用一个空数组,或者用一个空对象。
      // var tempObj =  Array.isArray(obj) ? [] : {}
      var tempObj = {}
      if (Array.isArray(obj)) {
        tempObj = []
      }

      // var tempObj =  {}
      // .... 操作
      for (var key in obj) {
        var t = obj[key]
        // 当要拷贝的内容还是一个对象或者是数组
        if (Array.isArray(t) || typeof t === "object") {
          tempObj[key] = deepCopy(obj[key])
        }
        else // 当要拷贝的内容只是一个基本类型的数据
        {
          tempObj[key] = t
        }
      }
      return tempObj
    }

    var newObj = deepCopy(obj);
    // 验证:
    // 给 obj1.hobby.添一个信息,不会影响obj.hobby. 
    obj1.hobby === obj.hobby; // false
// 简化写法
  function isObj(obj) {
    return (typeof obj === 'object' || typeof obj === 'function') && obj !== null
    }
 function deepCopy(obj) {
    var tempObj = Array.isArray(obj) ? [] : {}
    for (var key in obj) {
       tempObj[key] = isObj(obj[key]) ? deepCopy(obj[key]) : obj[key]
    }
    return tempObj
  }