• js 深拷贝和浅拷贝


    深浅拷贝对比

    深拷贝和浅拷贝是只针对Object和Array这样的对象数据类型的。

    深拷贝和浅拷贝的示意图大致如下:

    基本类型--名值存储在栈内存中,例如let a=1;

    当b=a复制的时候,栈内存会新开辟一个内存,如下:

     当你修改a=2时,b并不会收到影响,因为他有了自己独立的内存空间,但这并不是深拷贝,深拷贝和浅拷贝是只针对Object和Array这样的对象数据类型的。

    引用数据类型--名存在栈内存中,值存在于堆内存中,但是栈内存会提供一个引用的地址指向堆内存中的值

     当进行b=a拷贝时。其实是复制了a的引用地址,而并非是堆内存里面的值,

     

     当我们a[0]=1时进行数组修改时,由于a与b指向的是同一个地址,所以自然b也受了影响,这就是所谓的浅拷贝了。

    那,要是在堆内存中也开辟一个新的内存专门为b存放值,就像基本类型那样,起步就达到深拷贝的效果了

     

     浅拷贝只是复制了指向某个对象的指针,并没有复制对象的值,新旧对象还是共享同一个内存空间,但深拷贝会重新创建一个相同的对象,新对象有自己的指针和内存空间。我们可以多次使用同样的数据,数据修改后也不会发生错乱。

    浅拷贝的实现方式

    1. 直接赋值

    let obj = {username: 'kobe', age: 39, sex: {option1: '男', option2: '女'}};
      let obj1 = obj;
      obj1.sex.option1 = '不男不女'; // 修改复制的对象会影响原对象
      console.log(obj1, obj);

    2.Object.assign()

    let obj = {
        username: 'kobe'
        };
    let obj2 = Object.assign(obj);
    obj2.username = 'wade';
    console.log(obj);//{username: "wade"}

    3.Array.prototype.concat()

    let arr = [1, 3, {
        username: 'kobe'
        }];
    let arr2=arr.concat();    
    arr2[2].username = 'wade';
    console.log(arr);

    4.Array.prototype.slice()

    let arr = [1, 3, {
        username: ' kobe'
        }];
    let arr3 = arr.slice();
    arr3[2].username = 'wade'
    console.log(arr);

    关于Array的slice和concat方法的补充说明:Array的slice和concat方法不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组。

    • 如果该元素是个对象引用(不是实际的对象),slice 会拷贝这个对象引用到新的数组里。两个对象引用都引用了同一个对象。如果被引用的对象发生改变,则新的和原来的数组中的这个元素也会发生改变
    • 对于字符串、数字及布尔值来说(不是 String、Number 或者 Boolean 对象),slice 会拷贝这些值到新的数组里。在别的数组里修改这些字符串或数字或是布尔值,将不会影响另一个数组。
    let arr = [1, 3,[5,8] {
        username: ' kobe'
        }];
    let arr3 = arr.slice();
    arr3[1] = 2
    console.log(arr,arr3);

     对于这种浅层次的确实不受影响,但是深层次的呢?我们来看一看

    let arr = [1, 3,[5,8], {
        username: ' kobe'
        }];
    let arr4 = arr.concat();
    arr4[1] = 2
    arr4[2][0] = 9
    console.log(arr,arr4);

     

     可以看到这种二级属性还是没能拷贝成功。所以slice和concat方法并不是真正的深拷贝方法。

    深拷贝的实现方式

    1、递归

    递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝

    // 定义一个深拷贝函数  接收目标target参数
    function deepClone(target) {
        // 定义一个变量
        let result;
        // 如果当前需要深拷贝的是一个对象的话
        if (typeof target === 'object') {
        // 如果是一个数组的话
            if (Array.isArray(target)) {
                result = []; // 将result赋值为一个数组,并且执行遍历
                for (let i in target) {
                    // 递归克隆数组中的每一项
                    result.push(deepClone(target[i]))
                }
             // 判断如果当前的值是null的话;直接赋值为null
            } else if(target===null) {
                result = null;
             // 判断如果当前的值是一个RegExp对象的话,直接赋值    
            } else if(target.constructor===RegExp){
                result = target;
            }else {
             // 否则是普通对象,直接for in循环,递归赋值对象的所有值
                result = {};
                for (let i in target) {
                    result[i] = deepClone(target[i]);
                }
            }
         // 如果不是对象的话,就是基本数据类型,那么直接赋值
        } else {
            result = target;
        }
         // 返回最终结果
        return result;
    }
    let obj1 = {
            a: {
                c: /a/,
                d: undefined,
                b: null
            },
            b: function () {
                console.log(this.a)
            },
            c: [
                {
                    a: 'c',
                    b: /b/,
                    c: undefined
                },
                'a',
                3
            ]
        }
        let obj2 = deepClone(obj1);
    obj2.c[0].a='aaaa' console.log(obj1,obj2);

    可以看到obj里面的全部属性都拷贝成功,修改obj2,obj1的数据并不会受影响

     2、JSON.parse(JSON.stringify())

    let arr = [1, 2, {
        username: ' lihh'
    }];
    let arr4 = JSON.parse(JSON.stringify(arr));
    arr4[2].username = 'whhh'; 
    console.log(arr, arr4)

    原理: 用JSON.stringify将对象转成JSON字符串,再用JSON.parse()把字符串解析成对象,一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝。

    这种方法虽然可以实现数组或对象深拷贝,但不能处理函数

    let arr = [1, 2, {
        username: ' lihh'
    },function(){}];
    let arr4 = JSON.parse(JSON.stringify(arr));
    arr4[2].username = 'whhh'; 
    console.log(arr, arr4)

     因为JSON.stringify() 方法是将一个JavaScript值(对象或者数组)转换为一个 JSON字符串,不能接受函数

     缺点:如果对象中包含时间格式就会转为字符串、如果包含正则就会成为 undefined、如果包含function、undefined就会丢失。

     

    不积跬步无以至千里
  • 相关阅读:
    js数组中的reverse()方法
    JavaScript语言精粹知识点总结
    JavaScript高级程序设计学习笔记第十五章--使用Canvas绘图
    js中一些小知识点总结--持续更新
    怎么预览 GitHub 项目里的网页或 Demo?
    leetcode 6. ZigZag Conversion
    leetcode 67. Add Binary
    javascript:with的用法以及延长作用域链
    水平居中
    SetInterval与setTimeout
  • 原文地址:https://www.cnblogs.com/lyt0207/p/12082727.html
Copyright © 2020-2023  润新知