JavaScript 中克隆数组的方法
JavaScript 可以做很多事情,现在我们了解处理数组克隆。
扩展操作符(浅拷贝)
自从 ES6 推出以来,这是最受欢迎的方法。当在类似 React 和 Redux 的框架中使用,扩展操作符语法简洁且难以置信的好用。
const numbers = [1, 2, 3]
const numbersCopy = [...numbers]
注意:该方法不是安全地复制多维数组,Array/object 类型的值在复制时使用的是值的引用实例。
于是,良好的情况:
numbersCopy.push(4)
console.log(numbers, numbersCopy)
// [1, 2, 3] and [1, 2, 3, 4]
// numbers 是独立的
不好的情况:
const nestedNumbers = [[1], [2]]
const numbersCopy = [...nestedNumbers]
numbersCopy[0].push(300)
console.log(nestedNumbers, numbersCopy)
// [[1, 300], [2]]
// [[1, 300], [2]]
// 它们两个都发生改变,因为它们共享引用
for() 循环(浅拷贝)
相信这是最流行的,毕竟这是编程圈子里面通用的方法。
纯净或不纯净,陈述性或命令性,就可以完成工作!
const numbers = [1, 2, 3]
const numbersCopy = []
for (let 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]
// numbers 是独立的
不好的情况:
const nestedNumbers = [[1], [2]]
const numbersCopy = []
for (let i = 0; i < nestedNumbers.length; i++) {
numbersCopy[i] = nestedNumbers[i]
}
numbersCopy[0].push(300)
console.log(nestedNumbers, numbersCopy)
// [[1, 300], [2]]
// [[1, 300], [2]]
// 它们两个都发生改变,因为它们共享引用
while() 循环
与 for
相似。
const numbers = [1, 2, 3]
const numbersCopy = []
let 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]
// numbers 是独立的
不好的情况:
const nestedNumbers = [[1], [2]]
const numbersCopy = []
let i = -1
while (++i < nestedNumbers.length) {
numbersCopy[i] = nestedNumbers[i]
}
numbersCopy[0].push(300)
console.log(nestedNumbers, numbersCopy)
// [[1, 300], [2]]
// [[1, 300], [2]]
// 它们两个都发生改变,因为它们共享引用
Array.map(浅拷贝)
我们想到 map
函数。在数学领域,map
是将一组成另一种类型的设定,同时保持结构的概念。
众所周知,Array.map
意味着每次都返回一个相同长度的数组。要使数字列表加倍,可以使用 Array.map
配合 double
函数。
const numbers = [1, 2, 3]
const double = x => x * 2
numbers.map(double)
怎样克隆呢?
确实,这篇文章是关于克隆数组,为了复制数组,只需要在 map
的回调函数中返回元素自身。
const numbers = [1, 2, 3]
numberCopy = numbers.map(x => x)
注意:这同样是通过引用而不是值分配对象/数组。
Array.filter(浅拷贝)
Array.filter
函数像 map
那样返回一个数组,但它通常不会生成相同长度数组。
你是怎样过滤数据中的偶数的呢?
;[1, 2, 3].filter(x => x % 2 === 0)
// [2]
输入的数组的长度是 3,结果的长度是 1.
如果 filter
的回调函数总是返回 true
,于是同样实现了复制:
const numbers = [1, 2, 3]
const numbersCopy = numbers.filter(() => true)
每一个数组元素都测试通过,故得到返回,实现数组复制。
注意:这同样是通过引用而不是值分配对象/数组。多维数组的克隆复制不安全。
Array.reduce(浅拷贝)
我对使用 Array.reduce
来克隆数组感到一点感伤,因为它是比其它方法更强大。
const numbers = [1, 2, 3]
const numbersCopy = numbers.reduce((newArray, element) => {
newArray.push(element)
return newArray
}, [])
Array.reduce
通过轮询列表来转换初始值。上面的初始值是一个空数组,然后我们使用每一个元素填充它。该数组在回调函数中必须返回以用于下一次的迭代。
注意:这同样是通过引用而不是值分配对象/数组。多维数组的克隆复制不安全。
Array.slice(浅拷贝)
Array.slice
基于你提供的开始/结束索引返回数组的浅拷贝。如果我们想要开始的 3 个元素:
;[1, 2, 3, 4, 5].slice(0, 3)
// [1, 2, 3]
// Starts at index 0, stops at index 3
// 在索引 0 开始索引 3 结束
如果想要各道所有的元素,不设置任何参数:
const numbers = [1, 2, 3, 4, 5]
const numbersCopy = numbers.slice()
// [1, 2, 3, 4, 5]
注意:这是浅拷贝,同样是通过引用而不是值分配对象/数组。多维数组的克隆复制不安全。
JSON.parse 和 JSON.stringify(深拷贝)
JSON.stringify
转换对象为字符串。JSON.parse
转换 JSON 字符串为对象。组合它们可以转换对象为字符串,然后再反向过程会创建一个新数据结构。
注意:这是一个安全复制多维对象/数组的方法。
const nestedNumbers = [[1], [2]]
const numbersCopy = JSON.parse(JSON.stringify(nestedNumbers))
numbersCopy[0].push(300)
console.log(nestedNumbers, numbersCopy)
// [[1], [2]]
// [[1, 300], [2]]
// 这两个数组是完全不同的
Array.concat(浅拷贝)
Array.cancat
使用值或其它数组组合数组。
;[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]
注意:这同样是通过引用而不是值分配对象/数组。多维数组的克隆复制不安全。
Array.from(浅拷贝)
Array.from
可以转换可迭代对象和类数组对象为数组。
const numbers = [1, 2, 3]
const numbersCopy = Array.from(numbers)
// [1, 2, 3]
注意:这同样是通过引用而不是值分配对象/数组。多维数组的克隆复制不安全。