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 相关文章推荐
js类中获取外部函数名的方法
Aug 19 Javascript
JavaScript面向对象之体会[总结]
Nov 13 Javascript
js 实现菜单左右滚动显示示例介绍
Nov 21 Javascript
jQuery trigger()方法用法介绍
Jan 13 Javascript
微信小程序 设置启动页面的两种方法
Mar 09 Javascript
微信小程序 在线支付功能的实现
Mar 14 Javascript
bootstrap的工具提示实例代码
May 17 Javascript
JavaScript中如何判断一个值的类型
Sep 15 Javascript
vuex的使用及持久化state的方式详解
Jan 23 Javascript
Vue Router的懒加载路径的解决方法
Jun 21 Javascript
WebGL three.js学习笔记之阴影与实现物体的动画效果
Apr 25 Javascript
详解基于原生JS验证表单组件xy-form
Aug 20 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
PHP如何抛出异常处理错误
2011/03/02 PHP
PHP5下$_SERVER变量不再受magic_quotes_gpc保护的弥补方法
2012/10/31 PHP
thinkphp配置连接数据库技巧
2014/12/02 PHP
php内嵌函数用法实例
2015/03/20 PHP
php实现头像上传预览功能
2017/04/27 PHP
事件冒泡是什么如何用jquery阻止事件冒泡
2013/03/20 Javascript
JavaScript实现url地址自动检测并添加URL链接示例代码
2013/11/12 Javascript
javascript打开word文档的方法
2014/04/16 Javascript
我的Node.js学习之路(三)--node.js作用、回调、同步和异步代码 以及事件循环
2014/07/06 Javascript
jQuery实现当前页面标签高亮显示的方法
2015/03/10 Javascript
基于jQuery实现仿QQ空间送礼物功能代码
2016/05/24 Javascript
jQuery弹出div层过2秒自动消失
2016/11/29 Javascript
详解jQuery简单的表单应用
2016/12/16 Javascript
使用ionic切换页面卡顿的解决方法
2016/12/16 Javascript
Ajax基础知识详解
2017/02/17 Javascript
js for循环倒序输出数组元素的实例
2017/03/01 Javascript
详解nodejs异步I/O和事件循环
2017/06/07 NodeJs
JS实现常见的查找、排序、去重算法示例
2018/05/21 Javascript
详解Vue改变数组中对象的属性不重新渲染View的解决方案
2018/09/21 Javascript
解决layui-open关闭自身窗口的问题
2019/09/10 Javascript
vue 使用插槽分发内容操作示例【单个插槽、具名插槽、作用域插槽】
2020/03/06 Javascript
vant时间控件使用方法详解
2020/12/24 Javascript
线程和进程的区别及Python代码实例
2015/02/04 Python
pyenv命令管理多个Python版本
2017/03/26 Python
python WindowsError的错误代码详解
2017/07/23 Python
Python机器学习之scikit-learn库中KNN算法的封装与使用方法
2018/12/14 Python
Pyqt5实现英文学习词典
2019/06/24 Python
Python yield生成器和return对比代码实例
2020/04/20 Python
jenkins+python自动化测试持续集成教程
2020/05/12 Python
Python中socket网络通信是干嘛的
2020/05/27 Python
css3实现画半圆弧线的示例代码
2017/11/06 HTML / CSS
利用三角函数在canvas上画虚线的方法
2018/01/11 HTML / CSS
Linux常见面试题
2013/03/18 面试题
总裁办公室主任职责
2014/01/02 职场文书
2015年精神文明建设工作总结
2015/04/21 职场文书
房租涨价通知
2015/04/23 职场文书