先看下面一个js片段:
let arr = [ { title: "aa", }, { title: "bb", }, ]; let reArr = [...arr]; //是个新数组 let result = reArr.map((item) => { //map 也会生成新数组,正常情况下arr,reArr,result都是新数组,不相互干扰 item.title += "--first"; return item; }); console.log(arr, reArr, result); //结果……
写过C语言的开发,肯定知道指针的概念,很不幸JS里没有,可能唯一能算作指针的就是this了。我们对数组的指针有“地址引用”的说法。请看下图:
语法reArr=[...arr]是浅拷贝,只是拷贝了数组arr每个对象的位置属性,真实的数据源是在橙色的数据存储空间内,当我们修改对象值的时候,其实是修改了橙色的数据源,两个数组的共用对象值,因此arr也跟着改变了。
理解地址引用,我们就有解决方案了,让新的数组要深拷贝,或者完全生成一个新的数组,数据源也是新的。
把二级数据对象,从重新生成,生成新的地址引用
let result = reArr.map((item) => { return { ...item, title: item.title + "--first" }; //新的地址索引 });
缺点:只求修改两级,多级要多级在深拷贝,但是是最合理的处理方式,推荐
直接复制一个全新的数组对象,把数组先生成JSON字符串,再转回数组
let reArr = JSON.parse(JSON.stringify(arr)); //是个新数组 let result = reArr.map((item) => { item.title += "--first"; return item; });
优点:简单粗暴,个人不喜欢;
很多数组的方法都是 浅拷贝,不如之前经常用的数组常用方法,map,filter,splice,concat等都是浅拷贝,当数组是二位数组的时候,就要考虑深拷贝了。推荐全能的深拷贝常用函数:
"use strict"; // Method that will return the data type for any structure passed to it function getDataType(data) { // Use the objects toString method on the data. // This will return something like [object String] // Then we use .slice to grab the last portion of it (in this case the "string" bit) return Object.prototype.toString.call(data).slice(8, -1); } // Create a method to detect whether an object contains a circular reference function isCyclic(data) { // Create an array that will store the nodes of the array that have already been iterated over var seenObjects = []; function detect(data) { // If the data pass is an object if (data && getDataType(data) === "Object") { // If the data is already in the seen nodes array then we know there is a circular reference // Therefore return true if (seenObjects.indexOf(data) !== -1) { return true; } // Add the data to the seen objects array seenObjects.push(data); // Begin iterating through the data passed to the method for (var key in data) { // Recall this method with the objects key if ( Object.prototype.hasOwnProperty.call(data, key) === true && detect(data[key]) ) { return true; } } } return false; } // Return the method return detect(data); } export default function deepClone(data) { // If the data is null or undefined then we return undefined if (data === null || data === undefined) { return undefined; } // Get the data type and store it var dataType = getDataType(data); // If the data passed is a date object if (dataType === "Date") { // Create a new date object and set the time to what it was previously var dataDate = data; var clonedDate = new Date(); clonedDate.setTime(dataDate.getTime()); return clonedDate; } // If the data passed is an object if (dataType === "Object") { // Check for circular references, if there are then we just return the un-cloned data. if (isCyclic(data) === true) { return data; } // Create a new object that will store our copied data var copiedObject = {}; // Iterate over the objects keys for (var key in data) { // Clone the keys of each of the objects so that we can deeply copy and nested data structures // For example if an object has a key value that is an array // Add this cloned key value to the copiedObject we created earlier copiedObject[key] = deepClone(data[key]); } // Return the deeply copied object return copiedObject; } // If the data is an array if (dataType === "Array") { // Create a new array that will have no references to the one we want to copy var copiedArray = []; var dataArray = data; // Iterate over the arrays elements for (var i = 0; i < dataArray.length; i++) { // Push the arrays elements to this new array // First recall this method with the elements // This is so arrays of objects and other nested data structures get correctly cloned. copiedArray.push(deepClone(dataArray[i])); } // Return the cloned array return copiedArray; } // If it's any other data type like a string or number, they don't need cloning so we just return them else { return data; } }
上一篇:了解前端分分钟,一入前端毁终生
下一篇:前端脚手架如何搭建