最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Javascript的深浅拷贝原来是这样的 | 七日打卡

    正文概述 掘金(橙某人)   2021-01-16   527

    前言

    Javascript(下面简称JS)世界里将数据类型分为了两种:原始数据类型引用数据类型

    现共有八种数据类型:StringNumberBooleanNullUndefinedObjectSymbol(ES6)Bigint(ES10)

    • 原始数据类型:StringNumberBooleanNullUndefinedSymbolBigint
    • 引用数据类型:Object(Array、Function、Date、RegExp等等)。

    不同类型的存储方式:

    • 原始数据类型:原始数据类型的值在内存中占据固定大小,保存在栈内存中。
    • 引用数据类型:引用数据类型的值是对象,在栈内存中只是保存对象的变量标识符以及对象在堆内存中的储存地址,其内容是保存中堆内存中的。

    不同类型的拷贝方式:

    • 原始数据类型

      对于原始数据类型,从一个变量拷贝到另一个变量,只是很单纯的赋值过程,两者是不会有什么影响的。

      let a = 1;
      let b = a;
      a = 3;
      
      console.log(a, b); // 3 1
      
    • 引用数据类型

      而对于引用数据类型,从一个变量拷贝到另一个变量,本质是拷贝了对象的储存地址,两个变量最终都还是指向同一个对象。

      let a = {name: '橙某人'};
      let b = a;
      a.name = 'yd';
      
      console.log(a, b);
      
      let c = [1];
      let d = c;
      c[0] = 2;
      
      console.log(c, d);
      

    所以本章我们要讲的深浅拷贝针对是引用数据类型,下面我们就来仔细瞧瞧看看吧。

    浅拷贝 与 深拷贝

    概念

    为什么存在浅拷贝深拷贝
    简单一句话:“防止父对象数据被篡改。”

    • 浅拷贝:完成拷贝后可能存在彼此之间操作互相影响的就是浅拷贝。
    • 深拷贝:完成拷贝后彼此之间操作绝对不会有互相影响的就是深拷贝。

    浅拷贝

    • 数组浅拷贝 - concat()slice()
    let arr = [1, 2, 3];
    let newArr = arr.concat();
    newArr[0] = 20;
    console.log(arr, newArr); // [1, 2, 3] [20, 2, 3]
    
    let arr1 = [1, 2, 3];
    let newArr1 = arr1.slice();
    newArr1[0] = 20;
    console.log(arr1, newArr1); // [1, 2, 3] [20, 2, 3]
    
    • 对象浅拷贝
    function copy(object) {
      let o = {};
      for(let key in object) {
        o[key] = object[key];
      }
      return o;
    }
    let obj = {name: '橙某人'};
    let newObj = copy(obj);
    newObj.name = 'YD';
    console.log(obj, newObj); // {name: "橙某人"} {name: "YD"}
    

    懵了? 上面不是说不是说不互相影响的就是深拷贝吗?小编欺骗你?咋会呢? 不要着急。

    我在上面留了个心眼,就是浅拷贝可能存在彼此之间操作互相影响,也就是说浅拷贝也可能存在不会互相影响的情况。

    文字游戏?小编玩起了文字游戏?给我打。。。

    害,我也没有办法,这两个概念就是怎么,如果单纯考虑对象只有一层深度且也只是普通的原始数据类型值的话,那你说上面的例子是深拷贝,那我不反驳,哈哈。

    但他们确实是浅拷贝啦,如果是如下多层对象深度的话,就跑不了了。

    // 数组
    let arr = [1, 2, [1]];
    let newArr = arr.concat();
    newArr[2][0] = 10;
    console.log(arr, newArr); // [1, 2, [10]] [1, 2, [10]]
    
    let arr1 = [1, 2, [1]];
    let newArr1 = arr1.slice();
    newArr1[2][0] = 10;
    console.log(arr1, newArr1); // [1, 2, [10]] [1, 2, [10]]
    
    // 对象
    function copy(object) {
      let o = {};
      for(let key in object) {
        o[key] = object[key];
      }
      return o;
    }
    let obj = {name: '橙某人', hobby: {val: 'basketball'}};
    let newObj = copy(obj);
    newObj.hobby.val = 'football';
    console.log(obj, newObj); // {name: '橙某人', hobby: {val: 'football'}} {name: '橙某人', hobby: {val: 'football'}}
    

    对象多层级浅拷贝就无法处理了,这种情况就需要深拷贝了。

    深拷贝

    深拷贝就不是简单的拷贝引用地址了,而是在堆中重新分配内存,并且把源对象的所有属性都进行新建拷贝,以保证深拷贝的对象引用不包含任何原有对象或对象上的任何对象属性,复制后的对象与原来的对象是完全隔离的。

    JSON.parse()JSON.stringify()

    • 数组深拷贝 - JSON.parse()JSON.stringify()
    let arr = [1, 2, [1]];
    let newArr = JSON.parse(JSON.stringify(arr));
    newArr[2][0] = 10;
    console.log(arr, newArr); // [1, 2, [1]] [1, 2, [10]]
    
    let arr1 = [1, 2, [1]];
    let newArr1 = JSON.parse(JSON.stringify(arr1));
    newArr1[2][0] = 10;
    console.log(arr1, newArr1); // [1, 2, [1]] [1, 2, [10]]
    
    • 对象深拷贝 - JSON.parse()JSON.stringify()
    let obj = {name: '橙某人', hobby: {val: 'basketball'}};
    let newObj = JSON.parse(JSON.stringify(obj));
    newObj.hobby.val = 'football';
    console.log(obj, newObj); // {name: "yd", hobby: {val: 'basketball'}} {name: "yd", hobby: {val: 'football'}}
    

    利用JSON.parse()JSON.stringify()是不是非常简单粗暴?哈哈,但是呢。简单就肯定有弊端了,如:

    let arr = [() => {}, { b: () => {} }, new Date()];
    let newArr = JSON.parse(JSON.stringify(arr));
    console.log(newArr);
    
    let obj = {a: () => {}, b: new Date()};
    let newObj = JSON.parse(JSON.stringify(obj));
    console.log(newObj);
    

    Javascript的深浅拷贝原来是这样的 | 七日打卡

    可怕不? 函数不见了?日期类型变成字符串? 这就是弊端了,只能说且用且谨慎了。

    递归

    上面方式虽然比较简单方便,但用的时候还是要稍微想一下,省得被坑了。

    下面我们就用递归来实现下深拷贝,其中的思路:在对属性值进行拷贝的时候我们先判断下一下它的类型,如果是对象,我们就递归调用深拷贝函数,如果不是就直接拷贝过去,就怎么简单。

    // 深拷贝函数
    function deepCopy(obj) {
      if (typeof obj !== 'object') return;
      let result = obj instanceof Array ? [] : {};
      for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
          result[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
        }
      }
      return result;
    }
    

    测试代码。

    // 数组
    let arr = [1, 2, [1]];
    let newArr = deepCopy(arr);
    newArr[2][0] = 10;
    console.log(arr, newArr); // [1, 2, [1]] [1, 2, [10]]
    
    let arr1 = [1, 2, [1]];
    let newArr1 = deepCopy(arr1);
    newArr1[2][0] = 10;
    console.log(arr1, newArr1); // [1, 2, [1]] [1, 2, [10]]
    
    // 对象
    let obj = {name: 'yd', hobby: {val: 'basketball'}};
    let newObj = deepCopy(obj);
    newObj.hobby.val = 'football';
    console.log(obj, newObj); // {name: "yd", hobby: {val: 'basketball'}} {name: "yd", hobby: {val: 'football'}}
    

    然后我们来测试下那些特殊的函数、日期类型。

    let arr = [() => {}, { b: () => {} }, new Date()];
    let newArr = deepCopy(arr);
    console.log(newArr);
    
    let obj = {a: () => {}, b: new Date()};
    let newObj = deepCopy(obj);
    console.log(newObj);
    

    Javascript的深浅拷贝原来是这样的 | 七日打卡

    根据上图我们发现,函数是没有问题的,还是保留下来了,但是日期就完蛋了,变了个空对象,Why? 不要着急,是深拷贝函数deepCopy()出现了问题,我们改改。

    function deepCopy(obj) {
      if (typeof obj !== 'object') return;
      let result = obj instanceof Array ? [] : {};
      for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
          result[key] = typeof obj[key] === 'object' && !(obj[key] instanceof Date) ? deepCopy(obj[key]) : obj[key];
        }
      }
      return result;
    }
    

    Javascript的深浅拷贝原来是这样的 | 七日打卡

    这下正常了,明白了吧? 嘿嘿~ 不明白的自己悟吧,本章就差不多这样子啦,拜拜咯。
    (当然递归只是一种方式,网上一搜各种各样实现深拷贝的方式,绝对优雅,感兴趣可以慢慢研究一下。)

    Javascript的深浅拷贝原来是这样的 | 七日打卡


    起源地下载网 » Javascript的深浅拷贝原来是这样的 | 七日打卡

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元