这是在ES6中出现的新的运算符,自从出现以来,它已经成为最受欢迎的方法之一,它的语法实在是太简洁
numbers=[1, 2, 3]; numbersCopy=[...numbers];
注意:它不能安全的复制多维数组,数组/对象是通过引用复制而不是通过值复制
正例:
numbersCopy.push(4); console.log(numbers, numbersCopy); // [1, 2, 3] and [1, 2, 3, 4]
反例:
nestedNumbers=[[1], [2]]; numbersCopy=[...nestedNumbers]; numbersCopy[0].push(300); console.log(nestedNumbers, numbersCopy); // [[1, 300], [2]] // [[1, 300], [2]]
由于函数式编程的流行,现在应该用的比较少了
numbers=[1, 2, 3]; numbersCopy=[]; for (i=0; i < numbers.length; i++) { numbersCopy[i]=numbers[i]; }
注意:它也不能安全地复制多维数组。因为使用=运算符,因此它将通过引用而不是值来分配对象/数组。
正例:
numbersCopy.push(4); console.log(numbers, numbersCopy); // [1, 2, 3] and [1, 2, 3, 4]
反例:
nestedNumbers=[[1], [2]]; numbersCopy=[]; for (i=0; i < nestedNumbers.length; i++) { numbersCopy[i]=nestedNumbers[i]; } numbersCopy[0].push(300); console.log(nestedNumbers, numbersCopy);
和for循环类似
numbers=[1, 2, 3]; numbersCopy=[]; i=-1; while (++i < numbers.length) { numbersCopy[i]=numbers[i]; }
注意:它也通过引用而不是按值分配对象/数组。
正例:
numbersCopy.push(4); console.log(numbers, numbersCopy); // [1, 2, 3] and [1, 2, 3, 4]
反例:
nestedNumbers=[[1], [2]]; numbersCopy=[]; i=-1; while (++i < nestedNumbers.length) { numbersCopy[i]=nestedNumbers[i]; } numbersCopy[0].push(300); console.log(nestedNumbers, numbersCopy); // [[1, 300], [2]] // [[1, 300], [2]]
map会将数组转换成为另一个数组又同时保留结构的方法
numbers=[1, 2, 3]; double=(x)=> x * 2; numbers.map(double);
复制数组就更简单了
numbers=[1, 2, 3]; numbersCopy=numbers.map((x)=> x);
注意:它同样也通过引用而不是按值分配对象/数组。
和map一样,接受一个函数组作为参数,但是它会通过传递的函数过滤和筛选,这样不能保证得到数组长度不变,比如筛选出数组中的偶数
[1, 2, 3].filter((x)=> x % 2===0) // [2]
复制数组
numbers=[1, 2, 3]; numbersCopy=numbers.filter(()=> true);
注意:它同样也通过引用而不是按值分配对象/数组。
reduce 在循环列表时转换初始值
numbers=[1, 2, 3]; numbersCopy=numbers.reduce((newArray, element)=> { newArray.push(element); return newArray; }, []);
注意:它同样也通过引用而不是按值分配对象/数组。
slice需要你传递开始索引和结束索引
[1, 2, 3, 4, 5].slice(0, 3); // [1, 2, 3] // 0到3索引,得到1,2,3,不包括4
如果只想复制,不需要传递任何参数即可
numbers=[1, 2, 3, 4, 5]; numbersCopy=numbers.slice(); // [1, 2, 3, 4, 5]
注意:它同样也通过引用而不是按值分配对象/数组。
组合它们可以将对象转换为字符串,然后反转该过程可以创建全新的数据结构。
nestedNumbers=[[1], [2]]; numbersCopy=JSON.parse( JSON.stringify(nestedNumbers) ); numbersCopy[0].push(300); console.log(nestedNumbers, numbersCopy); // [[1], [2]] // [[1, 300], [2]] // 两个数组是分开的
注意:它是安全的复制方法
concat是数组之间相连接,例如
[1, 2, 3].concat(4); // [1, 2, 3, 4] [1, 2, 3].concat([4, 5]); // [1, 2, 3, 4, 5]
如果只是单纯的复制,那你可以不传递任何参数或者传递一个空数组即可
[1, 2, 3].concat(); // [1, 2, 3] [1, 2, 3].concat([]); // [1, 2, 3]
注意:它同样也通过引用而不是按值分配对象/数组。
可以将任何可迭代对象转换为数组,如下
numbers=[1, 2, 3]; numbersCopy=Array.from(numbers) // [1, 2, 3]
注意:它同样也通过引用而不是按值分配对象/数组。
本文着重探讨的是数组的复制,这些方法都可以用来做数组的拷贝,各有好坏,其实还有其他的一些方法来完成数组的复制,本文就介绍到这,如果对你有帮助,请点个关注吧!
在前端开发的浩瀚海洋中,JavaScript犹如一盏明灯,照亮了网页交互的每一个角落。作为现代Web开发不可或缺的一部分,掌握JavaScript的核心概念是每个前端工程师的基本功。本文旨在深入解析JavaScript中的几个关键点——二维数组的操作、引用类型的特性以及深克隆与浅克隆的区别,通过理论结合实践,帮助初学者构建坚实的基础。
二维数组是数组元素本身也是数组的数据结构,常用于模拟表格或矩阵。它们在处理网格布局、游戏地图等场景时尤为有用。
示例代码:
let matrix=[
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
console.log(matrix[1][2]); // 输出 6
JavaScript中的对象、数组、函数等都属于引用类型。它们存储的是指向数据的引用,而非数据本身,这意味着多个变量可以引用同一个对象。
示例代码:
let obj1={ value: 10 };
let obj2=obj1;
obj2.value=20;
console.log(obj1.value); // 输出 20
克隆是在不改变原对象的情况下复制对象的过程。浅克隆仅复制对象的第一层属性,而深克隆会递归复制所有层级的属性。
示例代码:
function shallowClone(obj) {
return Object.assign({}, obj);
}
function deepClone(obj) {
return JSON.parse(JSON.stringify(obj));
}
考虑一个游戏开发场景,二维数组可以用来表示游戏地图,其中每个元素代表地图上的一个位置。
示例代码:
let gameMap=[
['water', 'land', 'land'],
['land', 'forest', 'mountain'],
['mountain', 'water', 'land']
];
在事件监听器中传递对象时,利用引用类型可以实时反映对象状态的变化。
示例代码:
let data={ count: 0 };
document.getElementById('myButton').addEventListener('click', function() {
data.count++;
console.log(data.count);
});
在需要保持数据原始状态不变的场景下,如表单的初始值保存,使用深克隆可以创建一份完全独立的数据副本。
示例代码:
let originalFormValues={ name: 'John', age: 30 };
let formBackup=deepClone(originalFormValues);
深克隆会消耗更多的计算资源,对于复杂的对象,应评估是否真的需要复制每一层。
对于包含函数或循环引用的对象,使用JSON.parse和JSON.stringify进行深克隆可能失败,这时需要寻找更专业的克隆库或自定义克隆函数。
可以使用typeof检查,但更准确的方法是使用Object.prototype.toString.call()。
示例代码:
function isObject(value) {
return Object.prototype.toString.call(value)==='[object Object]';
}
确保在函数内部使用局部变量,避免直接修改传入的引用类型参数。
掌握JavaScript中的二维数组、引用类型和克隆技巧是构建高效、健壮的前端应用程序的关键。随着Web技术的不断演进,这些基础知识将为开发者提供强大的工具箱,应对未来可能出现的各种挑战。继续深化你的JavaScript技能,探索更多高级主题,如异步编程、模块化设计和性能优化,将使你在前端开发领域更加游刃有余。
意味着,当您尝试使用 const newArray=oldArray 复制它时,它将复制对原始数组的引用而不是复制值。
const beers=['', '', ''];
const beersCopy=beers;
console.log(beers===beersCopy);
// The console output will be true, because it's pointing to the exact value in memory.
浅拷贝数组
当拷贝或克隆数组时,值的数据类型是决定使用何种方法的关键。
如果您的数组仅包含原始不可变值(数字、布尔值、字符串等),您可以使用以下方法。
我们简单地遍历原始数组并将每个元素推送到新数组。
const arr=[1, 2, 3];
const newArr=[];
for (let i=0; i < arr.length; i++) {
newArr.push(arr[i]);
}
…(展开)
展开运算符是最快的方法之一,是在 ES6 中引入的。它简洁明了,是一种本地方法(对不起 lodash)。
const arr=[1, 2, 3];
const newArr=[...arr];
.slice()
slice() 方法将数组的一部分的浅拷贝返回到从头到尾选择的新数组对象中 arr.slice([start[, end]])。原始数组不会被修改。这是复制数组的最快方法。
const arr=[1, 2, 3];
const newArr=arr.slice();
Array.from
Array.from() 静态方法从类数组或可迭代对象(Map 或 Set)创建一个新的浅拷贝 Array 实例。
它还采用可选的映射函数作为第二个参数 Array.from(arrayLike [, mapFn [, thisArg]])。
const arr=[1, 2, 3];
const newArr=Array.from(arr);
_.clone()
lodash 的 _.clone(value) 方法创建了一个值的浅克隆。它表现良好,如果您已经在您的应用程序中使用 lodash,它是一个可行的选择。
// import lodash
const _=require('lodash');
const arr=[1, 2, 3];
const newArr=_.clone(arr);
Deep Copy Arrays
上面的所有方法都会创建数组的浅表副本。如果您将对象作为数组项,并且尝试进行浅拷贝,则将复制实际数组,但底层对象将通过引用传递给新数组。
让我们通过一个代码示例来看一下这个问题。
const arr=[
{
name: 'mario',
food: 'pizza',
},
{
name: 'luigi',
food: 'spaghetti',
},
];
const arrCopy=[...arr];
console.log(arr===arrCopy);
// 这将返回 false,因为数组已被复制(浅复制),内存中的新引用。
console.log(arr[0]===arrCopy[0]);
// 这将返回 true,因为引用指向内存中的相同位置。
有几种方法可以创建深度克隆。
jQuery.extend()
_.clonedeep()
JSON (stringify/parse)
jQuery.extend()
如果您仍在项目中使用 jQuery,则可以使用 jQuery 的扩展方法。
const arr=[
{
name: 'mario',
food: 'pizza',
},
{
name: 'luigi',
food: 'spaghetti',
},
];
const arrCopy=jQuery.extend(true, [], arr);
console.log(arr===arrCopy);
// This will return false, because the array has been copied (new reference in memory).
console.log(arr[0]===arrCopy[0]);
// This will return false, because the reference points to a new one in memory.
_.cloneDeep()
来自 Lodash 的方法 _.cloneDeep(value) 做的事情与 _.clone() 完全相同,除了它递归地克隆数组中的所有内容。
const arr=[
{
name: 'mario',
food: 'pizza',
},
{
name: 'luigi',
food: 'spaghetti',
},
];
// import lodash
const _=require('lodash');
const arrCopy=_.cloneDeep(arr);
console.log(arr===arrCopy);
// This will return false, because the array has been copied (new reference in memory).
console.log(arr[0]===arrCopy[0]);
// This will return false, because the reference points to a new one in memory.
JSON 方法
JavaScript 提供了用于序列化和反序列化的原生方法,基本上是将大多数数据类型转换为 JSON 字符串,然后将有效的 JSON 字符串转换为对象。
它可以这样使用:
const arr=[
{
name: 'mario',
food: 'pizza',
},
{
name: 'luigi',
food: 'spaghetti',
},
];
const arrCopy=JSON.parse(JSON.stringify(arr));
console.log(arr===arrCopy);
// This will return false, because the array has been copied (new reference in memory).
console.log(arr[0]===arrCopy[0]);
// This will return false, because the reference points to a new one in memory.
使用这种方法有一些优点和缺点。一个优点是它不需要像 Lodash 这样的第三方库。缺点是提供的数组数据需要可序列化,通过 JSON 进行序列化和反序列化需要一些时间,因此这不是最快的方法。
*请认真填写需求信息,我们会在24小时内与您取得联系。