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

    1.基本类型的值和引用类型的值

    基本类型值指的是存储在栈中的一些简单的数据段,在JavaScript中基本数据类型有String,Number,Undefined,Null,Boolean,在ES6中,又定义了一种新的基本数据类型Symbol,所以一共有6种

    基本类型是按值访问的,从一个变量复制基本类型的值到另一个变量后这2个变量的值是完全独立的,即使一个变量改变了也不会影响到第二个变量

    引用类型值是引用类型的实例,它是保存在堆内存中的一个对象,引用类型是一种数据结构,最常用的是Object,Array,Function类型,另外还有Date,RegExp,Error等,ES6同样也提供了Set,Map2种新的数据结构

    2.数据传递的方法:

        值传递:基本数据类型的数据不会发改变,因为基本数据类型一般存放在栈里面,值传递只是将数据拷贝了一份给另一个变量
        var a = 10;
        var b = a;
        b+=10;
        console.log(a);//10
        console.log(b);//20;
       引用传递:会改变内存中的数据,因为引用类型的数据都存放在堆里面,栈里面存放的是索引,拷贝的时候是拷贝的地址也就是索引
        var arr = [10,20,30,40];
        var newArr = arr;
        newArr[0] = 80;
        console.log(arr);//[80,20,30,40]
        console.log(newArr);//[80,20,30,40];
    

    3.深拷贝和浅拷贝

    浅拷贝:所谓的浅拷贝就是复制一份引用数据类型的地址,当改变了内存中数据的某一个值得话,也会影响到另一个对象
    深拷贝:所谓的深拷贝就是复制一份引用数据类型的数据,当改变了数据的某一个值得话,不会影响到另一个对象(注意深拷贝是拷贝的数据,而不是索引,浅拷贝拷贝的是索引而不是数据)

    区别:假设B复制了A,修改A的时候,看B是否发生变化:如果B变了便是浅拷贝,如果B没有变便是深拷贝

    以下是一些JavaScript提供的浅拷贝方法:

    3.1 Object.assign()
      Object.assign(target, ...sources)
    var target = {};
    var source = {a:1};
    Object.assign(target ,source);
    console.log(target); //{a:1}
    source.a = 2;
    console.log(source); //{a:2}
    console.log(target); //{a:1}

      Object.assign是一个浅拷贝,它只是在根属性(对象的第一层级)创建了一个新的对象,但是对于属性的值是仍是对象的话依然是浅拷贝

      Object.assign还有一些注意的点是:

    1. 不会拷贝对象继承的属性
    2. 不可枚举的属性
    3. 属性的数据属性/访问器属性
    4. 可以拷贝Symbol类型

      可以理解为Object.assign就是使用简单的=来赋值,遍历从右往左遍历源对象(sources)的所有属性用 = 赋值到目标对象(target)上

        3.2 扩展运算符

          var cloneObj = { ...obj };

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

      3.3 Array.prototype.slice()

      slice() 方法返回一个新的数组对象,这一对象是一个由 begin和 end(不包括end)决定的原数组的浅拷贝。原始数组不会被改变。

      在ES6以前,没有剩余运算符,Array.from的时候可以用 Array.prototype.slice将arguments类数组转为真正的数组,它返回一个浅拷贝后的的新数组

    Array.prototype.slice.call({0: "aaa", length: 1}) //["aaa"]
    
    let arr = [1,2,3,4]
    console.log(arr.slice() === arr); //false
    

        深拷贝

      将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象

      JSON.stringify()

      JSON.stringify()是目前前端开发过程中最常用的深拷贝方式,原理是把一个对象序列化成为一个JSON字符串,将对象的内容转换成字符串的形式再保存在磁盘上,再用JSON.parse()反序列化将JSON字符串变成一个新的对象
    通过JSON.stringify实现深拷贝有几点要注意
    • 拷贝的对象的值中如果有函数,undefined,symbol则经过JSON.stringify()序列化后的JSON字符串中这个键值对会消失
    • 无法拷贝不可枚举的属性,无法拷贝对象的原型链
    • 拷贝Date引用类型会变成字符串
    • 拷贝RegExp引用类型会变成空对象
    • 对象中含有NaN、Infinity和-Infinity,则序列化的结果会变成null
    • 无法拷贝对象的循环应用(即obj[key] = obj)
    function Obj() {
        this.func = function () {
            alert(1) 
        };
        this.obj = {a:1};
        this.arr = [1,2,3];
        this.und = undefined;
        this.reg = /123/;
        this.date = new Date(0);
        this.NaN = NaN
        this.infinity = Infinity
        this.sym = Symbol(1)
    }
    var obj1 = new Obj();
    Object.defineProperty(obj1,'innumerable',{
        enumerable:false,
        value:'innumerable'
    })
    console.log('obj1',obj1);
    var str = JSON.stringify(obj1);
    var obj2 = JSON.parse(str);
    console.log('obj2',obj2);
    

      

     虽说通过JSON.stringify()方法深拷贝对象也有很多无法实现的功能,但是对于日常的开发需求(对象和数组),使用这种方法是最简单和快捷的

    使用第三方库实现对象的深拷贝

    1.lodash

    2.jQuery

    自己来实现一个深拷贝函数

    递归

    这里简单封装了一个deepClone的函数,for in遍历传入参数的值,如果值是引用类型则再次调用deepClone函数,并且传入第一次调用deepClone参数的值作为第二次调用deepClone的参数,如果不是引用类型就直接复制
    var obj1 = {
        a:{
            b:1
        }
    };
    function deepClone(obj) {
        var cloneObj = {}; //在堆内存中新建一个对象
        for(var key in obj){ //遍历参数的键
           if(typeof obj[key] ==='object'){ 
              cloneObj[key] = deepClone(obj[key]) //值是对象就再次调用函数
           }else{
               cloneObj[key] = obj[key] //基本类型直接复制值
           }
        }
        return cloneObj 
    }
    var obj2 = deepClone(obj1);
    obj1.a.b = 2;
    console.log(obj2); //{a:{b:1}}
    

      

    但是还有很多问题

    • 首先这个deepClone函数并不能复制不可枚举的属性以及Symbol类型
    • 这里只是针对Object引用类型的值做的循环迭代,而对于Array,Date,RegExp,Error,Function引用类型无法正确拷贝
    • 对象循环引用成环了的情况

    可参考下面的 对象深拷贝和浅拷贝 作者自己实现的深拷贝

    参考:深拷贝与浅拷贝的区别  深拷贝与浅拷贝的区别 对象深拷贝和浅拷贝

  • 相关阅读:
    Qt状态机实例
    <STL> accumulate 与 自定义数据类型
    <STL> 容器混合使用
    散列表(C版)
    Canonical 要将 Qt 应用带入 Ubuntu
    <STL> set随笔
    C++ 文件流
    视频播放的基本原理
    <STL> pair随笔
    c++ 内存存储 解决char*p, char p[]的问题
  • 原文地址:https://www.cnblogs.com/dylAlex/p/12409082.html
Copyright © 2011-2022 走看看