IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 如何理解 slice() 方法是浅拷贝?深入数组浅拷贝里的小细节 -> 正文阅读

[数据结构与算法]如何理解 slice() 方法是浅拷贝?深入数组浅拷贝里的小细节

slice() 方法

数组截取办法: slice(),用于截取数组中的一部分,返回一个新的数组对象,不影响原数组。arr.slice(begin, end)slice 会提取原数组中索引从 beginend 的所有元素(包含 begin,但不包含 end)。 注意 ? ? ? slice()方法是浅拷贝,具体在下文做解释。

slice(),用于截取数组中的一部分,返回一个新的数组对象,不影响原数组。

注意 ? ? ? slice()方法是浅拷贝,具体在下文做解释。

arr.slice(begin, end)slice 会提取原数组中索引从 beginend 的所有元素(包含 begin,但不包含 end)。

begin 可选 是提取起始处的索引(从 0 开始),从该索引开始提取原数组元素。

  • 如果该参数为负数,则表示从原数组中的倒数第几个元素开始提取,slice(-2) 表示提取原数组中的倒数第二个元素到最后一个元素(包含最后一个元素)。

  • 如果省略 begin,则 slice 从索引 0 开始。

  • 如果 begin 超出原数组的索引范围,则会返回空数组。

end 可选 是提取终止处的索引(从 0 开始),在该索引处结束提取原数组元素。slice 会提取原数组中索引从 begin 到 end 的所有元素(包含 begin,但不包含 end)。

  • slice(1,4) 会提取原数组中从第二个元素开始一直到第四个元素的所有元素 (索引为 1, 2, 3的元素)。

  • 如果该参数为负数, 则它表示在原数组中的倒数第几个元素结束抽取。 slice(-2,-1) 表示抽取了原数组中的倒数第二个元素到最后一个元素(不包含最后一个元素,也就是只有倒数第二个元素)。

  • 如果 end 被省略,则 slice 会一直提取到原数组末尾。

  • 如果 end 大于数组的长度,slice 也会一直提取到原数组末尾。

如何理解 slice() 方法的浅拷贝

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

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

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

下面通过实例详细了解一下 slice()方法的浅拷贝:

下面的代码描述了修改数组的几种情况:

let menuTreeList = [{
                "id": "cfda8029dfb311e7b555201a068c6482",
                "name": "系统管理",
                "menuType": 0,
                "children": [{
                    "id": "3873ccc2dfda11e7b555201a068c6483",
                    "name": "菜单管理",
                    "menuType": 2,
                    "children": [{
                            "id": "18bf8d5df09511e78a57201a068c6484",
                            "name": "新增",
                            "menuType": 1
                        },
                        {
                            "id": "18bf8d5df09511e78a57201a068c6485",
                            "name": "修改",
                            "menuType": 1
                        }
                    ]
                }]
            },
            {
                "id": "cfda8029dfb311e7b555201a068c6486",
                "name": "设备管理",
                "menuType": 0,
                "children": [{
                        "id": "18bf8d5df09511e78a57201a068c6487",
                        "name": "新增",
                        "menuType": 1
                    },
                    {
                        "id": "18bf8d5df09511e78a57201a068c6488",
                        "name": "修改",
                        "menuType": 1
                    }
                ]
            }
        ]

 let a = menuTreeList.slice(0, 1);
 console.group("1.修改对象深层属性")
 // console.log('a :>> ', a);
 // 1.被引用的对象发生改变,则新的和原来的数组中的这个元素也会发生改变:
 //   修改原始值 menuTreeList[0] ,即修改对象的引用
 menuTreeList[0].children = [];
 // 验证: 
 // 原来的数组:
 console.log('被修改过的原来的数组 menuTreeList :>> ', menuTreeList);
 // slice方法产生的新数组:
 console.log('新的数组 a :>> ', a);
 console.groupEnd()



 let newArr = [1, 2, 3, 4, 5, 6];
 let b = newArr.slice(0, 4);
 console.group("2.验证数组元素为字符串、数字及布尔值时的情况")
 // console.log('b :>> ', b);
 // 2.对于字符串、数字及布尔值来说,slice 会拷贝这些值到新的数组里。
 //   在别的数组里修改这些字符串或数字或是布尔值,将不会影响另一个数组:
 newArr[0] = 0;
 newArr[1] = 0;
 newArr[2] = 0;
 newArr[3] = 0;
 // 原来的数组:
 console.log('被修改过的原来的数组 newArr :>> ', newArr);
 // slice方法产生的新数组:
 console.log('新的数组 b :>> ', b);
 console.groupEnd();



 let newArr3 = [{
     "id": "1",
     "name": "设备管理",
     "menuType": 0,
 }, {
     "id": "2",
     "name": "设备管理",
     "menuType": 0,
 }, {
     "id": "3",
     "name": "设备管理",
     "menuType": 0,
 }, {
     "id": "4",
     "name": "设备管理",
     "menuType": 0,
 }];

 let d = newArr3.slice(0, 3);
 console.group("3.修改简单对象的属性 验证")
 // console.log('d :>> ', d);
 newArr3[0].id = "111111111";
 newArr3[1].id = "111111111";
 newArr3[2].id = "111111111";
 // 原来的数组:
 console.log('被修改过的原来的数组 newArr3 :>> ', newArr3);
 // slice方法产生的新数组:
 console.log('新的数组 d :>> ', d);
 console.groupEnd();



 let newArr2 = [{
     "id": "1",
     "name": "设备管理",
     "menuType": 0,
 }, {
     "id": "2",
     "name": "设备管理",
     "menuType": 0,
 }, {
     "id": "3",
     "name": "设备管理",
     "menuType": 0,
 }, {
     "id": "4",
     "name": "设备管理",
     "menuType": 0,
 }];
 let c = newArr2.slice(0, 3);
 console.group("4.把数组简单对象置空 验证")
 // console.log('c :>> ', c);
 newArr2[0] = {};
 newArr2[1] = {};
 newArr2[2] = {};
 // 原来的数组:
 console.log('被修改过的原来的数组 newArr2 :>> ', newArr2);
 // slice方法产生的新数组:
 console.log('新的数组 c :>> ', c);
 console.groupEnd()



 let newArr4 = [{
     "id": "1",
     "name": "设备管理",
     "menuType": 0,
 }, {
     "id": "2",
     "name": "设备管理",
     "menuType": 0,
 }, {
     "id": "3",
     "name": "设备管理",
     "menuType": 0,
 }, {
     "id": "4",
     "name": "设备管理",
     "menuType": 0,
 }];

 let e = newArr4.slice(0, 3);
 console.group("5.把数组简单对象元素改为基本数据类型 验证")
 newArr4[0] = 0;
 newArr4[1] = 0;
 newArr4[2] = 0;
 // 原来的数组:
 console.log('被修改过的原来的数组 newArr4 :>> ', newArr4);
 // slice方法产生的新数组:
 console.log('新的数组 e :>> ', e);
 console.groupEnd()

输出结果:
在这里插入图片描述
在这里插入图片描述
为什么把数组简单对象元素改为基本数据类型把数组简单对象置空这两种情况下的数组都没有跟着改变呢?

这个问题我是向 CSDN博客专家「零一」学习的,字节跳动大佬,很多高质量原创文章,快来观摩👉?「零一」?👈

问题解析:

我们可以这样理解,[q, w, e],其实这个数组中第一个元素就是变量q,它指向的是一个对象

  • 如果修改该对象内部的属性,那么变量q仍然只是指向这个对象,对象的内存地址没变
  • 如果直接把数组中第一个元素修改为空对象,那么就相当于 q = {},那指向的肯定跟原来的不一样了

即原来数组里的元素改为了新的对象,使用了新的对象引用,新数组里的元素对象依然存储在堆里,使用的是旧的对象引用。

我们接着看下面这个例子:

  let newArr6 = [{
      "id": "1",
      "name": "设备管理",
      "menuType": 0,
  }, {
      "id": "2",
      "name": "设备管理",
      "menuType": 0,
  }, {
      "id": "3",
      "name": "设备管理",
      "menuType": 0,
  }, {
      "id": "4",
      "name": "设备管理",
      "menuType": 0,
  }];

  let f = newArr6.slice(0, 3);
  console.group("6.把数组简单对象设置为新的对象 验证")
  // console.log('c :>> ', c);

  newArr6[0] = {
      "sss": "4444",
      "aaaa": "对方是否",
  };
  newArr6[1] = {
      "sss": "4444",
      "aaaa": "对方是否",
  };;
  newArr6[2] = {
      "sss": "4444",
      "aaaa": "对方是否",
  };;

  // 原来的数组:
  console.log('被修改过的原来的数组 newArr6 :>> ', newArr6);
  // slice方法产生的新数组:
  console.log('新的数组 f :>> ', f);
  console.groupEnd()

输出结果:
在这里插入图片描述
其实不管是把对象改为基本数据类型,还是重新赋一个新的对象,他们都属于一类,都是修改了对象的指向

原来数组里的元素指向了新的内存引用,新数组里的元素指向的是旧的内存引用,所以原来数组里的元素改为一个基本数据类型值,或者改为一个新的对象,或者空对象,并不会对新数组产生影响,因为他们数组元素的内存指向已经不一样了。

基本对象引用赋值的浅拷贝

这里和基本的对象引用赋值做一个对比:

let menuTreeList = [{
         "id": "cfda8029dfb311e7b555201a068c6482",
         "name": "系统管理",
         "menuType": 0,
         "children": [{
             "id": "3873ccc2dfda11e7b555201a068c6483",
             "name": "菜单管理",
             "menuType": 2,
             "children": [{
                     "id": "18bf8d5df09511e78a57201a068c6484",
                     "name": "新增",
                     "menuType": 1
                 },
                 {
                     "id": "18bf8d5df09511e78a57201a068c6485",
                     "name": "修改",
                     "menuType": 1
                 }
             ]
         }]
     },
     {
         "id": "cfda8029dfb311e7b555201a068c6486",
         "name": "设备管理",
         "menuType": 0,
         "children": [{
                 "id": "18bf8d5df09511e78a57201a068c6487",
                 "name": "新增",
                 "menuType": 1
             },
             {
                 "id": "18bf8d5df09511e78a57201a068c6488",
                 "name": "修改",
                 "menuType": 1
             }
         ]
     }
 ]

 let a = menuTreeList;
 console.group("1.修改对象深层属性")
 menuTreeList[0].children = [];
 // 验证: 
 // 原来的数组:
 console.log('被修改过的原来的数组 menuTreeList :>> ', menuTreeList);
 console.log('新的数组 a :>> ', a);
 console.groupEnd()



 let newArr = [1, 2, 3, 4, 5, 6];

 let b = newArr;
 console.group("2.验证数组元素为字符串、数字及布尔值时的情况")
 newArr[0] = 0;
 newArr[1] = 0;
 newArr[2] = 0;
 newArr[3] = 0;

 // 原来的数组:
 console.log('被修改过的原来的数组 newArr :>> ', newArr);
 console.log('新的数组 b :>> ', b);
 console.groupEnd();



 let newArr3 = [{
     "id": "1",
     "name": "设备管理",
     "menuType": 0,
 }, {
     "id": "2",
     "name": "设备管理",
     "menuType": 0,
 }, {
     "id": "3",
     "name": "设备管理",
     "menuType": 0,
 }, {
     "id": "4",
     "name": "设备管理",
     "menuType": 0,
 }];

 let d = newArr3;
 console.group("3.修改简单对象的属性 验证")
 newArr3[0].id = "111111111";
 newArr3[1].id = "111111111";
 newArr3[2].id = "111111111";

 // 原来的数组:
 console.log('被修改过的原来的数组 newArr3 :>> ', newArr3);
 console.log('新的数组 d :>> ', d);
 console.groupEnd();

 let newArr2 = [{
     "id": "1",
     "name": "设备管理",
     "menuType": 0,
 }, {
     "id": "2",
     "name": "设备管理",
     "menuType": 0,
 }, {
     "id": "3",
     "name": "设备管理",
     "menuType": 0,
 }, {
     "id": "4",
     "name": "设备管理",
     "menuType": 0,
 }];

 let c = newArr2;
 console.group("4.把数组简单对象置空 验证")
 newArr2[0] = {};
 newArr2[1] = {};
 newArr2[2] = {};

 // 原来的数组:
 console.log('被修改过的原来的数组 newArr2 :>> ', newArr2);
 console.log('新的数组 c :>> ', c);
 console.groupEnd()


 let newArr4 = [{
     "id": "1",
     "name": "设备管理",
     "menuType": 0,
 }, {
     "id": "2",
     "name": "设备管理",
     "menuType": 0,
 }, {
     "id": "3",
     "name": "设备管理",
     "menuType": 0,
 }, {
     "id": "4",
     "name": "设备管理",
     "menuType": 0,
 }];

 let e = newArr4;
 console.group("5.把数组简单对象元素改为基本数据类型 验证")

 newArr4[0] = 0;
 newArr4[1] = 0;
 newArr4[2] = 0;

 // 原来的数组:
 console.log('被修改过的原来的数组 newArr4 :>> ', newArr4);
 console.log('新的数组 e :>> ', e);
 console.groupEnd()

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

参考

  • https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/slice
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-09-27 14:21:50  更:2021-09-27 14:23:06 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 6:00:41-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码