JavaScript中十种一步拷贝数组的方法实例详解


Posted in Javascript onApril 22, 2019

JavaScript中我们经常会遇到拷贝数组的场景,但是都有哪些方式能够来实现呢,我们不妨来梳理一下。

JavaScript中十种一步拷贝数组的方法实例详解

1、扩展运算符(浅拷贝)

自从ES6出现以来,这已经成为最流行的方法。它是一个很简单的语法,但是当你在使用类似于React和Redux这类库时,你会发现它是非常非常有用的。

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]]
// 由于公用引用,所以两个数组都被修改了,这是我们不希望的

2、for()循环(浅拷贝)

考虑到函数式编程变得越来越流行,我认为这种方法可能是最不受欢迎的。

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);
// [[1, 300], [2]]
// [[1, 300], [2]]
// 由于公用引用,所以两个数组都被修改了,这是我们不希望的

3、while()循环(浅拷贝)和for() 类似。

numbers = [1, 2, 3];
numbersCopy = [];
i = -1;
while (++i < numbers.length) {
 numbersCopy[i] = numbers[i];
}

4、Array.map(浅拷贝)

上面的for和while都是很“古老”的方式,让我们继续回到当前,我们会发现map方法。map源于数学,是将一个集合转换成另一种集合,同时保留结构的概念。

在英语中,它意味着Array.map 每次返回相同长度的数组。

numbers = [1, 2, 3];
double = (x) => x * 2;
numbers.map(double);

当我们使用map方法时,需要给出一个callback函数用于处理当前的数组,并返回一个新的数组元素。

和拷贝数组有什么关系呢?

当我们想要复制一个数组的时候,只需要在map的callback函数中直接返回原数组的元素即可。

numbers = [1, 2, 3];
numbersCopy = numbers.map((x) => x);

如果你想更数学化一点,(x) => x叫做恒等式。它返回给定的任何参数。

identity = (x) => x;
numbers.map(identity);
// [1, 2, 3]

同样的,处理对象和数组的时候是引用而不是值复制。

5、Array.filter(浅拷贝)

Array.filter方法同样会返回一个新数组,但是并不一定是返回同样长度的,这和我们的过滤条件有关。

[1, 2, 3].filter((x) => x % 2 === 0)
// [2]

当我们的过滤条件总是true时,就可以用来实现拷贝。

numbers = [1, 2, 3];
numbersCopy = numbers.filter(() => true);
// [1, 2, 3]

同样的,处理对象和数组的时候是引用而不是值复制。

6、Array.reduce(浅拷贝)

其实用reduce来拷贝数组并没有展示出它的实际功能,但是我们还是要将其能够拷贝数组的能力说一下的

numbers = [1, 2, 3];
numbersCopy = numbers.reduce((newArray, element) => {
 newArray.push(element);
 return newArray;
}, []);

reduce() 方法对数组中的每个元素执行一个由您提供的reducer函数,将其结果汇总为单个返回值。

上面我们的例子中初始值是一个空数组,我们在遍历原数组的时候来填充这个空数组。该数组必须要从下一个迭代函数的执行后被返回出来。

同样的,处理对象和数组的时候是引用而不是值复制。

7、Array.slice(浅拷贝)

slice 方法根据我们指定的start、end的index从原数组中返回一个浅拷贝的数组。

[1, 2, 3, 4, 5].slice(0, 3);
// [1, 2, 3]
// Starts at index 0, stops at index 3
// 当不给定参数时,就返回了原数组的拷贝
numbers = [1, 2, 3, 4, 5];
numbersCopy = numbers.slice();
// [1, 2, 3, 4, 5]

同样的,处理对象和数组的时候是引用而不是值复制。

8、JSON.parse & JSON.stringify(深拷贝)

JSON.stringify将一个对象转成字符串;
JSON.parse将转成的字符串转回对象。

将它们组合起来可以将对象转换成字符串,然后反转这个过程来创建一个全新的数据结构。

nestedNumbers = [[1], [2]];
numbersCopy = JSON.parse(
 JSON.stringify(nestedNumbers)
);
numbersCopy[0].push(300);
console.log(nestedNumbers, numbersCopy);
// [[1], [2]]
// [[1, 300], [2]]
// These two arrays are completely separate!

这个可以安全地拷贝深度嵌套的对象/数组

几种特殊情况

1、如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式。而不是时间对象;

var test = {
 name: 'a',
 date: [new Date(1536627600000), new Date(1540047600000)],
};
let b;
b = JSON.parse(JSON.stringify(test))
console.log(b)

2、如果obj里有RegExp、Error对象,则序列化的结果将只得到空对象;

const test = {
 name: 'a',
 date: new RegExp('\\w+'),
};
// debugger
const copyed = JSON.parse(JSON.stringify(test));
test.name = 'test'
console.log('ddd', test, copyed)

3、如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失;

const test = {
 name: 'a',
 date: function hehe() {
 console.log('fff')
 },
};
// debugger
const copyed = JSON.parse(JSON.stringify(test));
test.name = 'test'
console.error('ddd', test, copyed)

4、如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null

5、JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor;

function Person(name) {
 this.name = name;
 console.log(name)
}
const liai = new Person('liai');
const test = {
 name: 'a',
 date: liai,
};
// debugger
const copyed = JSON.parse(JSON.stringify(test));
test.name = 'test'
console.error('ddd', test, copyed)

9、Array.cancat(浅拷贝)

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]

同样的,处理对象和数组的时候是引用而不是值复制。

10、Array.from(浅拷贝)

可以将任何可迭代对象转换为数组。给一个数组返回一个浅拷贝。

console.log(Array.from('foo'))
// ['f', 'o', 'o']
numbers = [1, 2, 3];
numbersCopy = Array.from(numbers)
// [1, 2, 3]
同样的,处理对象和数组的时候是引用而不是值复制。

小结

上面这些方法都是在使用一个步骤来进行拷贝。如果我们结合一些其他的方法或技术能够发现还有很多的方式来实现数组的拷贝,比如一系列的拷贝工具函数等。

以上所述是小编给大家介绍的JavaScript中十种一步拷贝数组的方法实例详解,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!

Javascript 相关文章推荐
初探jquery——表单应用范例
Feb 20 Javascript
DOM和XMLHttpRequest对象的属性和方法整理
Jan 04 Javascript
使用JSON.parse将json字符串转换成json对象的时候会出错
Sep 04 Javascript
AngularJS 中的指令实践开发指南(一)
Mar 20 Javascript
vuejs父子组件通信的问题
Jan 11 Javascript
Bootstrap实现的标签页内容切换显示效果示例
May 25 Javascript
JavaScript变量类型以及变量作用域详解
Aug 14 Javascript
在vue项目中使用Nprogress.js进度条的方法
Jan 31 Javascript
基于js中的存储键值对以及注意事项介绍
Mar 30 Javascript
小程序实现留言板
Nov 02 Javascript
layui table 多行删除(id获取)的方法
Sep 12 Javascript
JavaScript对象字面量和构造函数原理与用法详解
Apr 18 Javascript
vue watch关于对象内的属性监听
Apr 22 #Javascript
vue项目中仿element-ui弹框效果的实例代码
Apr 22 #Javascript
对于防止按钮重复点击的尝试详解
Apr 22 #Javascript
Vue render函数实战之实现tabs选项卡组件
Apr 22 #Javascript
详解Vue依赖收集引发的问题
Apr 22 #Javascript
JS大坑之19位数的Number型精度丢失问题详解
Apr 22 #Javascript
Vue $mount实战之实现消息弹窗组件
Apr 22 #Javascript
You might like
discuz 首页四格:最新话题+最新回复+热门话题+精华文章插件
2007/08/19 PHP
php利用cookie实现访问次数统计代码
2011/05/19 PHP
PHP对象实例化单例方法
2017/01/19 PHP
PHP实现的简单排列组合算法应用示例
2017/06/20 PHP
在Laravel中使用GuzzleHttp调用第三方服务的API接口代码
2019/10/15 PHP
PHP实现随机发扑克牌
2020/04/22 PHP
jQuery 图像裁剪插件Jcrop的简单使用
2009/05/22 Javascript
javascript 函数速查表
2010/02/07 Javascript
javascript 模式设计之工厂模式详细说明
2010/05/10 Javascript
整理8个很棒的 jQuery 倒计时插件和教程
2011/12/12 Javascript
jquery ajaxSubmit 异步提交的简单实现
2014/02/28 Javascript
Nodejs Post请求报socket hang up错误的解决办法
2014/09/25 NodeJs
java中String类型变量的赋值问题介绍
2016/03/23 Javascript
jQuery之动画效果大全
2016/11/09 Javascript
angularjs点击图片放大实现上传图片预览
2017/02/24 Javascript
jQuery中animate()的使用方法及解决$(”body“).animate({“scrollTop”:top})不被Firefox支持的问题
2017/04/04 jQuery
VUE element-ui 写个复用Table组件的示例代码
2017/11/18 Javascript
JavaScript基于面向对象实现的猜拳游戏
2018/01/03 Javascript
vue 之 css module的使用方法
2018/12/04 Javascript
JavaScript函数式编程(Functional Programming)声明式与命令式实例分析
2019/05/21 Javascript
详解vue项目中实现图片裁剪功能
2019/06/07 Javascript
Vue使用Proxy监听所有接口状态的方法实现
2019/06/07 Javascript
javascript头像上传代码实例
2019/09/28 Javascript
Vue清除定时器setInterval优化方案分享
2020/07/21 Javascript
Vue 解决在element中使用$notify在提示信息中换行问题
2020/11/11 Javascript
Python面向对象之继承原理与用法案例分析
2019/12/31 Python
美国知名女性服饰品牌:New York & Company
2017/03/23 全球购物
全球最大的房车租赁市场:Outdoorsy
2018/09/19 全球购物
学期自我鉴定范文
2013/10/01 职场文书
物流管理应届生求职信
2013/11/07 职场文书
大学生实习自我鉴定
2013/12/11 职场文书
护理目标管理责任书
2014/07/25 职场文书
2014年优秀党员材料
2014/12/18 职场文书
2016年小学党支部创先争优活动总结
2016/04/05 职场文书
python 机器学习的标准化、归一化、正则化、离散化和白化
2021/04/16 Python
Vite + React从零开始搭建一个开源组件库
2022/06/25 Javascript