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 相关文章推荐
Array.slice()与Array.splice()的返回值类型
Oct 09 Javascript
File, FileReader 和 Ajax 文件上传实例分析(php)
Apr 27 Javascript
jQuery链式操作如何实现以及为什么要用链式操作
Jan 17 Javascript
浅析XMLHttpRequest的缓存问题
Dec 13 Javascript
基于JavaScript如何制作遮罩层对话框
Jan 26 Javascript
Vue.js每天必学之内部响应式原理探究
Sep 07 Javascript
Node接收电子邮件的实例代码
Jul 21 Javascript
Vue项目中quill-editor带样式编辑器的使用方法
Aug 08 Javascript
JavaScript设计模式之职责链模式应用示例
Aug 07 Javascript
JavaScript创建防篡改对象的方法分析
Dec 30 Javascript
微信小程序代码上传、审核发布小程序
May 18 Javascript
JQuery事件冒泡和默认行为代码实例
May 13 jQuery
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
PHPnow安装服务[apache_pn]失败的问题的解决方法
2010/09/10 PHP
php在页面中调用fckeditor编辑器的方法
2011/06/10 PHP
比较好用的PHP防注入漏洞过滤函数代码
2012/04/11 PHP
php操作redis中的hash和zset类型数据的方法和代码例子
2014/07/05 PHP
PHP使用Session遇到的一个Permission denied Notice解决办法
2014/07/30 PHP
php常用字符串比较函数实例汇总
2014/11/24 PHP
php自定义时间转换函数示例
2016/12/07 PHP
js操作ajax返回的json的注意问题!
2010/02/23 Javascript
IE与Firefox在JavaScript上的7个不同句法分享
2011/10/30 Javascript
JavaScript 用Node.js写Shell脚本[译]
2012/09/20 Javascript
SwfUpload在IE10上不出现上传按钮的解决方法
2013/06/25 Javascript
jQuery EasyUI datagrid实现本地分页的方法
2015/02/13 Javascript
动态加载jQuery的两种方法实例分析
2015/07/17 Javascript
angularjs 表单密码验证自定义指令实现代码
2016/10/27 Javascript
详解VueJS应用中管理用户权限
2018/02/02 Javascript
Node.js模拟发起http请求从异步转同步的5种用法
2018/09/26 Javascript
Vue render函数实战之实现tabs选项卡组件
2019/04/22 Javascript
jQuery位置选择器用法实例分析
2019/06/28 jQuery
JS实现动态倒计时功能(天数、时、分、秒)
2019/12/12 Javascript
[48:46]完美世界DOTA2联赛PWL S2 SZ vs FTD.C 第二场 11.19
2020/11/19 DOTA
[05:09]DOTA2-DPC中国联赛2月22日Recap集锦
2021/03/11 DOTA
使用python BeautifulSoup库抓取58手机维修信息
2013/11/21 Python
python获取指定网页上所有超链接的方法
2015/04/04 Python
Scrapy爬虫实例讲解_校花网
2017/10/23 Python
opencv python 2D直方图的示例代码
2018/07/20 Python
Python运维开发之psutil库的使用详解
2018/10/18 Python
对Python 除法负数取商的取整方式详解
2018/12/12 Python
Python 绘制酷炫的三维图步骤详解
2019/07/12 Python
python openvc 裁剪、剪切图片 提取图片的行和列
2019/09/19 Python
使用python快速实现不同机器间文件夹共享方式
2019/12/22 Python
tensorflow 固定部分参数训练,只训练部分参数的实例
2020/01/20 Python
HTML5通过调用canvas对象的getContext()方法来获取绘图环境
2014/06/23 HTML / CSS
大型车展策划方案
2014/02/01 职场文书
薪资证明范本
2015/06/19 职场文书
Python OpenCV实现图形检测示例详解
2022/04/08 Python
windows server2012 R2下安装PaddleOCR服务的的详细步骤
2022/09/23 Servers