•  阅读 3 分钟

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]

注意:这同样是通过引用而不是值分配对象/数组。多维数组的克隆复制不安全。

本文由 吳文俊 翻译,原文地址

> 分享并评论 Twitter / Facebook
> cd ..