zoukankan      html  css  js  c++  java
  • 浅拷贝与深拷贝

    问题缘起

    如果给一个变量赋值一个对象,那么新变量和原对象变量将会是同一个引用,其中一方改变,另一方也会改变。

    该问题可以用浅拷贝来解决。但是浅拷贝只能解决对象的第一层的引用问题(或数组的第一维),如果接下去的属性还是对象的话那么还是同一个引用。这就需要引入深拷贝。

    对象的深浅拷贝

    1. 浅拷贝: Object.assign({},sourceObj) 或 {...sourceObj}

    当sourceObj的属性是值的时候,效果和深拷贝看起来一样;但是如果sourceObj的属性是另外一个对象时,就是浅拷贝了。

    (1)使用Object.assign()实现浅拷贝

    let a = {
      age:1
    }
    let b = Object.assign({}, a);
    a.age = 2;
    console.log(b.age);//1
    

    (2)使用{...obj}实现浅拷贝

    let a = {
      age: 1
    }
    let b = {...a};
    a.age = 2;
    console.log(b.age);//1
    

    2. 深拷贝:JSON.parse(JSON.stringify(sourceObj)) 或loadash库的deepClone()方法

    上面已经说过,浅拷贝只能解决第一层的引用问题,如果属性还是一个对象,那么还是会共享一个引用:

    let a = {
      age: 1,
      jobs: {
        main:'frontend'
      }
    }
    
    let b = {...a};
    a.jobs.main = 'backend';
    console.log(b.jobs.main);//'backend'
    
    

    该问题通常可以用JSON.parse(JSON.stringify(sourceObj))或lodash的cloneDeep(Obj)方法来解决

    (1)使用JSON.parse(JSON.stringify(sourceObj))实现深拷贝

    let a = {
      age: 1,
      jobs: {
        main:'frontend'
      }
    }
    let b = JSON.parse(JSON.stringify(a));
    a.jobs.main = 'backend';
    console.log(b.jobs.main);//'frontend'
    
    

    JSON.parse(JSON.stringify(sourceObj))实现深拷贝的局限:

    • 会忽略undefined
    • 不能处理函数,会忽略掉函数
    • 不能处理循环引用的对象,会报错TypeError

    (2)使用loadash的deepClone()方法

    如果数据中出现了以上3中情况,那么可以考虑使用loadash的深拷贝函数。

    参考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

    数组的深浅拷贝

    1.浅拷贝:arr.slice()或arr.concat():

    浅拷贝,即到数组只有一维的时候,且每一项不是对象的话这样拷贝看起来和深拷贝效果一样

    (1) 使用Array.prototype.slice()实现浅拷贝

    const a = [1,2,3]
    const b = a.slice();
    a.push(4);
    console.log(a);//[1,2,3,4]
    console.log(b);//[1,2,3]
    

    浅拷贝一个数组,这样操作新数组时,就不会改变原数组。

    slice不修改原数组,只返回一个浅复制了原数组总的元素的一个新数组。原数组的元素会按照下述规则拷贝:

    • 如果该元素是个对象引用 (不是实际的对象),slice 会拷贝这个对象引用到新的数组里。两个对象引用都引用了同一个对象。如果被引用的对象发生改变,则新的和原来的数组中的这个元素也会发生改变。
    • 对于字符串、数字及布尔值来说(不是 String、Number 或者 Boolean 对象),slice 会拷贝这些值到新的数组里。在别的数组里修改这些字符串或数字或是布尔值,将不会影响另一个数组。

    如果向两个数组任一中添加了新元素,则另一个不会受到影响。

    (2)使用 Array.prototype.concat()实现浅拷贝

    const a = [1,2,3]
    const b = a.concat();
    a.push(4);
    console.log(a);//[1,2,3,4]
    console.log(b);//[1,2,3]
    

    concat方法创建一个新的数组,它由被调用的对象中的元素组成,每个参数的顺序依次是该参数的元素(如果参数是数组)或参数本身(如果参数不是数组)。它不会递归到嵌套数组参数中。

    concat方法不会改变this或任何作为参数提供的数组,而是返回一个浅拷贝,它包含与原始数组相结合的相同元素的副本。 原始数组的元素将按照如下规则复制到新数组中:

    • 对象引用(而不是实际对象):concat将对象引用复制到新数组中。 原始数组和新数组都引用相同的对象。 也就是说,如果引用的对象被修改,则更改对于新数组和原始数组都是可见的。 这包括也是数组的数组参数的元素。
    • 数据类型如字符串,数字和布尔(不是String,Number 和 Boolean 对象):concat将字符串和数字的值复制到新数组中。

    2. 深拷贝:JSON.parse(JSON.stringify(arr)) 或loadash的deepClone()方法

    同对象的深拷贝。

    引申阅读:拷贝不变性的重要意义

    参见https://reactjs.org/tutorial/tutorial.html#why-immutability-is-important

    参考资料:

    https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/slice
    https://github.com/wengjq/Blog/issues/3
    https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/slice
    https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/concat

  • 相关阅读:
    linux 解压tgz 文件指令
    shell 脚本没有执行权限 报错 bash: ./myshell.sh: Permission denied
    linux 启动solr 报错 Your Max Processes Limit is currently 31202. It should be set to 65000 to avoid operational disruption.
    远程查询批量导入数据
    修改 MZTreeView 赋权节点父节点选中子节点自动选中的问题
    关于乱码的问题解决记录
    我的网站优化之路
    对设计及重构的一点反思
    我的五年岁月
    奔三的路上
  • 原文地址:https://www.cnblogs.com/Bonnie3449/p/9510431.html
Copyright © 2011-2022 走看看