我们经常会用到对一个数组或对象的拷贝,而不是操作原数组或对象。下面总结几种js数组和对象深浅拷贝的几种方式:
一、es5实现数组和对象的浅拷贝与深拷贝
1.数组和对象浅拷贝:
function shallowCopy(source){
if (typeof source !== 'object') {
throw TypeError('the source must be an array or object')
}
let target = Array.isArray(source) ? [] : {};
for (let key in source){
//原型链上的属性不拷贝
if(source.hasOwnProperty(key)){
target[key] = source[key]
}
}
return target;
}
2.数组和对象深拷贝:
function deepCopy(source){
if(typeof source !== 'object'){
throw TypeError('the source must be an array or object');
}
let target = Array.isArray(source) ? [] :{};
for(let key in source){
if(source.hasOwnProperty(key)){//不拷贝原型链上的属性
if(typeof source[key] === 'object'){
//递归调用多维数组、对象
target[key] = deepCopy(source[key]);
}else{
target[key] = source[key];
}
}
}
return target;
}
二、使用扩展运算符[...]实现浅拷贝
es6中扩展运算符是实现数组和对象浅拷贝的最简单的实现方式
浅拷贝:其实我下面集成一下反而显得繁琐不简洁,使用的时候根据数组或对象直接【...source】{...source}才是最简洁的。
function shallowCopy(source){
if(typeof source !== 'object'){
throw TypeError('the source must be an array or object');
}
if(Array.isArray(source)){
return [...source];
}else{
return {...source};
}
}
深拷贝:这个玩意用来实现浅拷贝是最好的,用这个实现深拷贝?想啥呢!!!
三、使用Array.from实现数组的浅拷贝和深拷贝
Array.from
方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。实际应用中,常见的类似数组的对象是 DOM 操作返回的 NodeList 集合,以及函数内部的arguments
对象。Array.from
都可以将它们转为真正的数组。
扩展运算符背后调用的是遍历器接口(Symbol.iterator
),如果一个对象没有部署这个接口,就无法转换。Array.from
方法还支持类似数组的对象。所谓类似数组的对象,本质特征只有一点,即必须有length
属性。因此,任何有length
属性的对象,都可以通过Array.from
方法转为数组,而此时扩展运算符就无法转换。
数组浅拷贝:Array.from实现浅拷贝其实跟用扩展运算符原理类似
function shallowCopy(source) {
if(Array.isArray(source)){
return Array.from(source);
}else{
throw TypeError('the source must be an array');
}
}
数组深拷贝:利用Array.from的第二个参数回调函数进行递归调用
Array.from
还可以接受第二个参数,作用类似于数组的map
方法,用来对每个元素进行处理,将处理后的值放入返回的数组。
Array.from(arrayLike, x => x * x);
// 等同于
Array.from(arrayLike).map(x => x * x);
Array.from([1, 2, 3], (x) => x * x)
// [1, 4, 9]
所以可以利用这个map实现数组深拷贝:
function deepCopy(source) {
return Array.isArray(source) ? Array.from(source, arguments.callee) : source;
}
四、数组和对象深拷贝黑科技:JSON.parse()和JSON.stringify()
function deepCopy(source){
return JSON.parse(JSON.stringify(source));
}
但是这个黑科技是有缺点的:具体见 详情
- 如果对象中有函数如({mm:function(){...}})不会被拷贝
- 会将对象原型链上的属性一并给拷贝了