javascript关于浅拷贝、深拷贝解析应用,数组的深拷贝

JavaScript/前端
47
0
0
2024-11-01

🍀浅拷贝、深拷贝

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

浅拷贝仅仅复制了指向某个对象的指针,并不复制对象本身,新对象与旧对象还是共享同一块内存,修改其中一个对象,另一个对象也会随之变化。 深拷贝会另外创造一个一模一样的对象,新对象跟旧对象不共享内存,修改其中一个对象不会影响到另一个对象。

在js当中,使用 ‘=’ 复制,就是js数组的浅拷贝。修改效果如下

var a=[1,0,0,8,6];
var b=a;
console.log(b);  //输出的是1,0,0,8,6
a[0]=5;
console.log(b);  //输出的是5,0,0,8,6

🍀实现数组的深拷贝

JSON.parse(JSON.stringify()) 方式

let arr = [5, 2, 9];
let arr2 = JSON.parse(JSON.stringify(arr));
arr2[2] = 0;
console.log(arr, arr2); // [5, 2, 9]  [5, 2, 0]

原理: 用JSON.stringify将对象转成JSON字符串,再用JSON.parse()把字符串解析成对象,这样可以生成新的对象,新对象会开辟新内存栈,实现深拷贝。

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

递归生成

   // 定义检测数据类型的功能函数
   function checkedType(target) {
     return Object.prototype.toString.call(target).slice(8, -1)
   }
   // 实现深度克隆---对象/数组
   function clone(target) {
     // 判断拷贝的数据类型
     // 初始化变量result 成为最终克隆的数据
     let result, targetType = checkedType(target)
     if (targetType === 'object') {
       result = {}
     } else if (targetType === 'Array') {
       result = []
     } else {
       return target
     }
     // 遍历目标数据
     for (let i in target) {
       // 获取遍历数据结构的每一项值。
       let value = target[i]
       // 判断目标结构里的每一值是否存在对象/数组
       if (checkedType(value) === 'Object' ||
         checkedType(value) === 'Array') { 
          // 对象/数组里嵌套了对象/数组
          // 继续遍历获取到value值
         result[i] = clone(value)
       } else { 
         // 获取到value值是基本的数据类型或者是函数。
         result[i] = value;
       }
     }
     return result
   }

concat 方法 concat() 方法用于连接两个或多个数组。 该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。

let arr = [5, 2, 9];
let arr2 = arr.concat();
arr2[2] = 0;
console.log(arr, arr2); // [5, 2, 9]  [5, 2, 0]

slice 方法

slice() 方法可从已有的数组中返回选定的元素。arrayObject.slice(start,end)方法返回一个新的数组,包含从 start 到 end (不包括该元素,数学上来讲是左闭右开,即包含左,不含右)的 arrayObject 中的元素。

let arr = [5, 2, 9];
let arr2 = arr.slice(0);
arr2[2] = 0;
console.log(arr, arr2); // [5, 2, 9]  [5, 2, 0]

ES6扩展运算符

...扩展运算符是ES6的语法。 但是需要注意的是:用扩展运算符对数组或者对象进行拷贝时,只能扩展和深拷贝第一层的值,对于第二层极其以后的值,扩展运算符将不能对其进行打散扩展,也不能对其进行深拷贝,即拷贝后和拷贝前第二层中的对象或者数组仍然引用的是同一个地址,其中一方改变,另一方也跟着改变。

let arr = [5, 2, 9,{
   name: ' kobe'
}];
let [...arr2] = arr;
arr2[2] = 0;
arr2[3].name = 'tom';
console.log(arr, arr2);