IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    JavaScript - 深浅拷贝

    发表于 2022-09-02 00:00:00
    love 0

    前言

    JS 中的数据类型可分为两种:

    • 基本类型: undefined , null , Boolean , String , Number , Symbol
    • 引用类型: Object , Array , Date , Function , RegExp 等

    不同类型的存储方式:

    • 基本类型:基本类型值在内存中占据固定大小,保存在 栈内存 中
    • 引用类型:引用类型的值是对象,保存在 堆内存 中,而 栈内存 中存储的是对象的 变量标识符 以及对象在 堆内存 中的存储地址

    不同类型的复制方式:

    • 基本类型:从一个变量向另外一个新变量复制基本类型的值,会创建这个值的一个副本,并将该副本复制给新变量
    • 引用类型:从一个变量向另一个新变量复制引用类型的值,其实复制的是储存地址,最终两个变量最终都指向同一个对象

    深浅拷贝的区别

    • 浅拷贝:仅仅是复制了引用,彼此之间的操作会互相影响
    • 深拷贝:在堆中重新分配内存,不同的地址,相同的值,互不影响

    浅拷贝

    对象的浅拷贝是属性与拷贝的源对象属性共享相同的引用(指向相同的底层值)的副本。因此,当你更改源对象或副本时,也可能导致另一个对象发生更改。与之相比,在深拷贝中,源对象和副本是完全独立的。

    赋值
    function shallowClone(object) {
      // 只拷贝对象
      if (!object) return object
    
      // 根据 object 的类型判断是新建一个数组还是对象
      const newObject = Array.isArray(object) ? [] : {}
    
      // 遍历 object,并且判断是 object 的属性才拷贝
      for (let key in object) {
        if (object.hasOwnProperty(key)) {
          newObject[key] = object[key]
        }
      }
    
      return newObject
    }
    
    const obj = {
      a: 'hello',
      b: {
        c: 'js',
      },
    }
    
    const newObj = shallowClone(obj)
    newObj.a = 'HELLO'
    newObj.b.c = 'JS'
    
    console.log(obj.a) // => hello
    console.log(obj.b.c) // => JS
    
    Object.assign
    const obj = {
      a: 'hello',
      b: {
        c: 'js',
      },
    }
    
    const newObj = Object.assign({}, obj)
    newObj.a = 'HELLO'
    newObj.b.c = 'JS'
    
    console.log(obj.a) // => hello
    console.log(obj.b.c) // => JS
    
    扩展运算符(...)
    const obj = {
      a: 'hello',
      b: {
        c: 'js',
      },
    }
    
    const newObj = { ...obj }
    newObj.a = 'HELLO'
    newObj.b.c = 'JS'
    
    console.log(obj.a) // => hello
    console.log(obj.b.c) // => JS
    

    深拷贝

    在 JavaScript 中,深克隆是指对一个对象或数组进行完整的拷贝,包括嵌套的对象和数组。深克隆的实现有多种方式,具体选择取决于数据结构的复杂程度和性能要求。

    JSON.parse(JSON.stringify(...))
    const obj = {
      a: 'hello',
      b: {
        c: 'js',
      },
    }
    
    const newObj = JSON.parse(JSON.stringify(obj))
    newObj.a = 'HELLO'
    newObj.b.c = 'JS'
    
    console.log(obj.a) // => hello
    console.log(obj.b.c) // => js
    

    局限性:

    会忽略 undefined 会忽略 symbol 不能序列化函数 不能解决循环引用的对象

    递归
    function deepClone(obj) {
      if (obj === null) return obj
      if (obj instanceof Date) return new Date(obj)
      if (obj instanceof RegExp) return new RegExp(obj)
      if (typeof obj !== 'object') return obj
    
      const cloneObj = new obj.constructor()
      for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
          cloneObj[key] = deepClone(obj[key])
        }
      }
      return cloneObj
    }
    
    const obj = {
      a: 'hello',
      b: {
        c: 'js',
      },
    }
    
    const newObj = deepClone(obj)
    newObj.a = 'HELLO'
    newObj.b.c = 'JS'
    
    console.log(obj.a) // => hello
    console.log(obj.b.c) // => js
    

    局限性:

    有爆栈的风险 大对象性能问题 循环引用

    structuredClone

    全局的 structuredClone() 方法使用结构化克隆算法将给定的值进行深拷贝。

    const obj = {
      a: 'hello',
      b: {
        c: 'js',
      },
    }
    
    const newObj = structuredClone(obj)
    newObj.a = 'HELLO'
    newObj.b.c = 'JS'
    
    console.log(obj.a) // => hello
    console.log(obj.b.c) // => js
    


沪ICP备19023445号-2号
友情链接