• js-深拷贝浅拷贝


    深拷贝浅拷贝可以考察一个人的很多方面,例如:基本功,逻辑能力,编码能力;

    在实际工作中的应用:比如用于页面展示的数据状态,与需要传给后端的数据包中,有部分字段的值不一致的话,就需要在传参时根据接口文档覆写那几个字段的值。最常见的可能就是 status 这个参数了。界面上的展示需要 Boolean 值,而后端同学希望拿到的是 Number 值,1 或者 0。为了不影响展示效果,往往就需要深拷贝一下,再进行覆写,否则界面上就会因为某些值的变化,出现奇怪的现象。

    一、赋值

    JavaScript原始数据类型:number,string,boolean,null,undefind,symbol他们的赋值很简单,且赋值后两个变量互不影响

    let test1 = 'JavaScript';
    let test2 = test1;
    // test2: JavaScript
    test1 = 'JavaScript_change';
    // test2: JavaScript
    // test1: JavaScript_change

    另外的引用数据类型:object和array,深拷贝和浅拷贝的出现就与这两个数据类型有关。

    const obj={a:1,b:2};
    const obj2=obj;
    obj2.a=3;
    console.log(obj.a);//3

    依照赋值的思路,对 Object 引用类型进行拷贝,就会出问题。很多情况下,这不是我们想要的。这时,就需要用浅拷贝来实现了。

    二、浅拷贝

    理解:创建一个新的对象,把原有的对象属性值,完整地拷贝过来。其中包括了原始类型的值,还有引用类型的内存地址。

    const obj = {a:1, b:2};
    const obj2 = Object.assign({}, obj);
    obj2.a = 3;
    console.log(obj.a); // 1

    改变了obj2:a的属性,但是obj:a的属性没有发生改变;

    可是这样的拷贝还有瑕疵:

    const arr = [{a:1,b:2}, {a:3,b:4}];
    const newArr = [].concat(arr);
    
    newArr.length = 1; // 为了方便区分,只保留新数组的第一个元素
    console.log(newArr); // [{a:1,b:2}]
    console.log(arr); // [{a:1,b:2},{a:3,b:4}]
    
    newArr[0].a = 123; // 修改 newArr 中第一个元素的a
    console.log(arr[0]); // {a: 123, b: 2},竟然把 arr 的第一个元素的 a 也改了

    对象的object.assign(),数组的 Array.prototype.slice() Array.prototype.concat(),还有 ES6 的 扩展运算符,都有类似的问题,它们都属于浅拷贝。这一点,在实际工作中处理数据的组装时,要格外注意。

    所以浅拷贝可以这样理解:只拷贝第一层的原始类型值,和第一层的引用类型的地址。

    三、深拷贝

    我们希望当拷贝多层级的对象时也能实现互不影响的效果。所以,深拷贝的概念也就油然而生了。我们将深拷贝定义为:拷贝所有的属性值,以及属性地址指向的值的内存空间。

    也就是说:当遇到对象时,就再新开一个对象,然后将第二层源对象的属性值,完整的拷贝到这个新开的对象中。

    按照浅拷贝的思路,很容易就想到了递归调用。所以,就可以封装一个深拷贝的方法:

    function deepClone(obj) {
        if(!obj && typeof obj !== 'object'){
            return;
        }
        var newObj= toString.call(obj) === '[object Array]' ? [] : {};
        for (var key in obj) {
            if (obj[key] && typeof obj[key] === 'object') {
                newObj[key] = deepClone(obj[key]);
            } else {
                newObj[key] = obj[key];
            }
        }
        return newObj;
    }

    结果:

    let arr = [{a:1,b:2}, {a:3,b:4}];
    let newArr = deepClone(arr);
    
    newArr.length = 1; // 为了方便区分,只保留新数组的第一个元素
    console.log(newArr); // [{a:1, b:2}]
    console.log(arr); // [{a:1, b:2}, {a:3, b:4}]
    
    newArr[0].a = 123; // 修改 newArr 中第一个元素的 a
    console.log(arr[0]); // {a:1, b:2}

    达到了想要的效果;

    但是,这个方法貌似存在引用丢失问题,比如:

    var b = {};
    var a = {a1: b, a2: b};
    
    a.a1 === a.a2 // true
    
    var c = clone(a);
    c.a1 === c.a2 // false

    四、利用JSON解决深拷贝问题

    let newArr2 = JSON.parse(JSON.stringify(arr));
    console.log(arr[0]); // {a:1, b:2}
    newArr2[0].a = 123;
    console.log(arr[0]); // {a:1, b:2}

    JOSN.parse()方法将JSON字符串转化为对象,JSON.stringify()方法将JavaScript字符串转化为对象。

    但是,JSON 内部用了递归的方式,数据一但过多,就会有递归爆栈的风险。

    五、lodash的_.clone和_.deepclone

    const _ = require('lodash');//*

    本质上的原因是对象引用的是地址,直接赋值会把引用地址也复制给新值。
    浅复制只会将对象的各个属性进行依次复制,会把引用地址也复制。
    深拷贝是会递归源数据,把新值的引用地址给换掉。

    const _ = require('lodash')
    var objects = [{ 'a': 1 }, { 'b': 2 }];
    var shallow = _.clone(objects);
    shallow[0].a = 333;
    console.log(shallow,objects)//a=333,a=333
    console.log(shallow[0] === objects[0])//true
    const _ = require('lodash')
    var objects = [{ 'a': 1 }, { 'b': 2 }];
    var shallow = _.cloneDeep(objects);
    shallow[0].a = 333;
    console.log(shallow,objects)//a=333,a=1
    console.log(shallow[0] === objects[0])//false

    object.assign方法:

    object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。

    object.assign方法的第一个参数是目标对象,剩下所有的参数都是源对象。

    如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。

    如果只有一个参数该方法会直接返回该参数,若参数不是对象,则会先转成对象然后返回。

  • 相关阅读:
    idea打开了多个项目,多idea窗口相互切换的快捷键
    idea中的springboot项目如何不用重新编译,自动热部署
    线程基本使用--Thread内部方法调用start
    java如何快速创建List
    抓包工具Charles使用
    postman添加Cookie
    Ubuntu使用记录
    Intellij idea使用总结
    navicat 生成注册码( 仅供学习使用 )
    前端常用技术总结
  • 原文地址:https://www.cnblogs.com/czh64/p/12011089.html
Copyright © 2020-2023  润新知