[译] JavaScript 数组方法:变异 vs. 非变异

本文翻译自:JavaScript Array Methods: Mutating vs. Non-Mutating

JavaScript 提供了许多方式去增加、删除和替换数组中的元素,有些会作用到原数组本身,有些则不是——它们会返回一个新数组。

接下来,我会分别列举如何使用变异或非变异方法来实现这三个操作。文章末尾还会展示如何通过非变异方法 array.map() 来遍历数组并转化其中的元素。

虽然在这我没有列举一个详尽的列表,但是下面基本包含了数组所有的基础操作方式。

注意:接下来的内容中,当需要使用非变异方法时,我会使用 const 来声明数组,否则会用 let。尽管你可以改变一个用 const 声明的数组而且也不会抛错,但我还是喜欢用 const 显式地告诉大家这个值不会改变。

警告:看这篇文章的时候,要特别留意它们的区别:

  1. array.splice()改变原始数组,而
  2. array.slice()不会改变原始数组。

增加元素:变异方法

为数组增加元素的变异方法是 array.push()array.unshift()

// since the array will be mutated,
// use 'let' rather than 'const'
let mutatingAdd = ['a', 'b', 'c', 'd', 'e'];

mutatingAdd.push('f'); // ['a', 'b', 'c', 'd', 'e', 'f']
mutatingAdd.unshift('z'); // ['z', 'a', 'b', 'c', 'd', 'e']

上面的代码:

  • array.push() 增加一个元素到数组末尾
  • array.unshift() 增加一个元素到数组开头

增加元素:非变异方法

有两个办法可以在不改变原始数组的情况下增加元素。

第一个是 array.concat()

// since we will not be mutating,
// use const
const arr1 = ['a', 'b', 'c', 'd', 'e'];

const arr2 = arr1.concat('f'); // ['a', 'b', 'c', 'd', 'e'. 'f']
console.log(arr1); // ['a', 'b', 'c', 'd', 'e']

第二个方法需要借助 JavaScript 的展开运算符。展开运算符是数组前的三个点(...)。

// since we will not be mutating,
// use const
const arr1 = ['a', 'b', 'c', 'd', 'e'];

const arr2 = [...arr1, 'f']; // ['a', 'b', 'c', 'd', 'e', 'f']
const arr3 = ['z', ...arr1]; // ['z', 'a', 'b', 'c', 'd', 'e']

向上面这样使用展开运算符时,将会从原数组中拷贝所有元素放到新的数组中。

在第 5 行,我们从 arr1 取得了所有元素的拷贝,然后放到了新数组中,最后把 'f' 放在末尾。
在第 6 行,过程与上面一样,只不过新元素 'z' 放在了数组中其它元素的前面。

删除元素:变异方法

为数组删除元素的变异方法是 array.pop()array.shift()

// since the array will be mutated,
// use 'let' rather than 'const'
let mutatingRemove = ['a', 'b', 'c', 'd', 'e'];
mutatingRemove.pop(); // ['a', 'b', 'c', 'd']
mutatingRemove.shift(); // ['b', 'c', 'd']

上面的代码:

  • array.pop() 删除数组中的最末的元素
  • array.shift() 删除数组中开头的元素

array.pop()array.shift() 方法都返回被删除的元素。所以你可以抓取删除的元素并存到一个变量上。

let mutatingRemove = ['a', 'b', 'c', 'd', 'e'];

const returnedValue1 = mutatingRemove.pop();
console.log(mutatingRemove); // ['a', 'b', 'c', 'd']
console.log(returnedValue1); // 'e'

const returnedValue2 = mutatingRemove.shift();
console.log(mutatingRemove); // ['b', 'c', 'd']
console.log(returnedValue2); // 'a'

还可以用 array.splice() 来删除数组中的多个元素。

let mutatingRemove = ['a', 'b', 'c', 'd', 'e'];
mutatingRemove.splice(0, 2); // ['c', 'd', 'e']

这里的 mutatingRemove.splice(0, 2) 使用了两个参数(也可以是多个参数,下面还会再提到它)。

  1. 第一个参数是 splice 的起始索引位置。
  2. 第二个参数是想要删除的数量。

在上面的例子里,从 mutatingRemove 数组中删除了两个(第二个参数)元素,删除的元素起始于索引 0(第一个参数)。

就像 array.pop()array.shift() 一样,array.splice() 方法同样返回被删除的元素。

let mutatingRemove = ['a', 'b', 'c', 'd', 'e'];
let returnedItems = mutatingRemove.splice(0, 2);
console.log(mutatingRemove); // ['c', 'd', 'e']
console.log(returnedItems) // ['a', 'b']

删除元素:非变异方法

JavaScript 中的 array.filter() 方法会从原数组中创建一个新数组,但是这个新数组只包含符合特定要求的元素。

// since we will not be mutating,
// use const
const arr1 = ['a', 'b', 'c', 'd', 'e'];

const arr2 = arr1.filter(a => a !== 'e'); // ['a', 'b', 'd', 'f']
// OR
const arr2 = arr1.filter(a => {
  return a !== 'e';
}); // ['a', 'b', 'd', 'f']

上面的代码中,条件是「!== 'e'」,所以新数组(arr2)跟原始数组类似,只是仅包含符合「 !== 'e'」条件的元素。


*一些箭头函数的特性:
单行箭头函数(第 5 行)中,「return」关键字是隐式存在的,所以不需要显式写出来。

但是,在多行箭头函数(第 7~9 行)中,你需要显式的返回一个值。


另一种删除数组元素的非变异方法是使用 array.slice()(不要跟 array.splice() 搞混了)。

array.slice() 接受两个参数。

  1. 第一个参数是开始拷贝的起始索引位置。
  2. 第二个参数是拷贝结束的索引位置。结束位置的元素并不会包含进去(前开后闭)。
// since we will not be mutating, 
// use const
const arr1 = ['a', 'b', 'c', 'd', 'e'];
const arr2 = arr1.slice(1, 5) // ['b', 'c', 'd', 'e']
const arr3 = arr1.slice(2) // ['c', 'd', 'e']

上面的第 4 行(const arr2 = arr1.slice(1, 5)),arr2 是由自 arr1 的索引 1 起,到 5 之前(例如索引 4)的元素拷贝而来。

第 5 行(const arr3 = arr1.slice(2))是一种常见的方式。如果使用 array.slice() 没有传入第二个参数,那么该方法会从传入的索引起一直拷贝到数组结束。

替换元素:变异方法

如果你知道要替换的元素索引,那么可以直接用 array.splice() 去替换。

所以,我们需要至少三个参数来完成它。

  1. 第一个参数是开始替换的索引位置。
  2. 第二个参数是想要删除的元素个数。
  3. 第三个之后的参数是想要插入到数组中的元素。
// since the array will be mutated, 
// use 'let' rather than 'const'
let mutatingReplace = ['a', 'b', 'c', 'd', 'e'];
mutatingReplace.splice(2, 1, 30); // ['a', 'b', 30, 'd', 'e']
// OR
mutatingReplace.splice(2, 1, 30, 31); // ['a', 'b', 30, 31, 'd', 'e']

第 4 行(mutatingReplace.splice(2, 1, 30))将 'c' 替换为 30
第 6 行(mutatingReplace.splice(2, 1, 30, 31))删除了 'c',然后又插入了 3031 两个元素。

替换元素:非变异方法

我们可以使用 array.map() 来创建一个新数组,同时我们还能检查每一个元素或者按照特定要求来替换元素。

// since we will not be mutating, 
// use const
const arr1 = ['a', 'b', 'c', 'd', 'e']
const arr2 = arr1.map(item => {
  if(item === 'c') {
    item = 'CAT';
  }
  return item;
}); // ['a', 'b', 'CAT', 'd', 'e']

上面的代码基于 arr1 创建了一个新数组,并把所有的 'c' 都替换成了 'CAT'

通过 array.map() 转换数据

array.map()是一个强大的方法,可以在不影响原始数据的前提下做数据转换。

// since we will not be mutating, 
// use const
const origArr = ['a', 'b', 'c', 'd', 'e'];
const transformedArr = origArr.map(n => n + 'Hi!'); // ['aHi!', 'bHi!', 'cHi!', 'dHi!', 'eHi!']
// OR
const transformedArr = origArr.map(n => {
  return n * 2;
})// ['aHi!', 'bHi!', 'cHi!', 'dHi!', 'eHi!']
console.log(origArr); // ['a', 'b', 'c', 'd', 'e']; // orignal array is intact

-- EOF --

此条目发表在 前端开发 分类目录,贴了 , , 标签。将固定链接加入收藏夹。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

+ 28 = 37