在 JavaScript 中,你经常需要从数组中移除元素,无论是队列数据结构还是 React State。
在本文的前半部分,你将学习所有允许你在不更改原始数组的情况下从数组中移除元素的方法。事实上,这也是你最常要做的事情。例如,如果你不想更改 React State。或者在代码的其他部分使用了数组,改变数组会导致意想不到的问题。
不过,为了完整起见,本文的后半部分将列出从数组中就地移除项的方法。这些方法确实会改变数组本身。
如何在不更改数组的情况下从数组中移除元素
如果你有一个输入数组,比如作为函数参数的数组,那么根据最佳实践,你不应该更改数组,而是创建一个新数组。
有几种方法可以在不更改数组的情况下从数组中移除特定项。
为了避免对数组进行改变,我们将创建一个新的数组,但不包含要移除的元素。
你可以使用以下方法:
Array.prototype.slice()
Array.prototype.slice()
加上Array.prototype.concat()
Array.prototype.filter()
for
循环和Array.prototype.push()
让我们详细看看如何在不改变原有数组的情况下,使用上述每一种方法从数组中移除一个元素。
使用 slice 删除数组中的第一个元素
如果要删除数组中的第一个元素,可以在名为 arr
的数组上使用 Array.prototype.slice()
:arr.slice(1)
。
下面是一个完整的示例,你想从包含字母表前 6 个字母的数组中移除第一个元素。
// 初始数组
const arrayOfLetters = ['a', 'b', 'c', 'd', 'e', 'f'];
// 数组被复制,不包含第一个元素
const copyWithoutFirstElement = arrayOfLetters.slice(1);
// arrayOfLetters 没有被改变
console.log(arrayOfLetters) // ['a', 'b', 'c', 'd', 'e', 'f']
// copyWithoutFirstElement 包含字母 b 到 f
console.log(copyWithoutFirstElement) // ['b', 'c', 'd', 'e', 'f']
slice
方法可以使用一个数字作为参数,在这种情况下,它会从该索引复制到数组的末尾。因此,使用 arrayOfLetters.slice(1)
将创建一个不包含第一个元素的 arrayOfLetters
数组副本。
使用 slice 删除数组的最后一个元素
如果要删除的元素是数组的最后一个元素,可以在名为 arr
的数组上使用 Array.prototype.slice()
:arr.slice(0, -1)
。
下面是一个完整的示例,使用的是上面的字母数组,从包含前 6 个字母的数组开始。
const arrayOfLetters = ['a', 'b', 'c', 'd', 'e', 'f'];
const copyWithoutLastElement = arrayOfLetters.slice(0, -1);
// arrayOfLetters 没有被改变
console.log(arrayOfLetters) // ['a', 'b', 'c', 'd', 'e', 'f']
console.log(copyWithoutLastElement) // ['a', 'b', 'c', 'd', 'e']
slice
方法最多需要两个参数。slice
的第一个索引表示要从哪个索引开始复制,第二个参数表示要复制到哪个元素,但并不包含它。
slice
接受负数索引,从末尾开始计数。这意味着 -1
表示最后一个索引。因此,从 0
到 -1
意味着创建一个从索引 0
到(但不包括)最后一个索引的副本。最终的结果是,副本中不包括最后一个元素。
使用 slice 和 concat 在数组的任意位置删除元素
如果想创建一个缺少任意索引处元素的副本,可以使用 Array.prototype.slice
和 Array.prototype.concat
:arrayOfLetters.slice(0, n).concat(arrayOfLetters.slice(n+1))
,其中 n
是要删除元素的索引。
const arrayOfLetters = ['a', 'b', 'c', 'd', 'e', 'f'];
const halfBeforeTheUnwantedElement = arrayOfLetters.slice(0, 2)
const halfAfterTheUnwantedElement = arrayOfLetters(3);
const copyWithoutThirdElement = halfBeforeTheUnwantedElement.concat(halfAfterTheUnwantedElement);
// arrayOfLetters 没有被改变
console.log(arrayOfLetters) // ['a', 'b', 'c', 'd', 'e', 'f']
console.log(copyWithoutFifthElement) // ['a', 'b', 'd', 'e', 'f']
这次使用 slice
是将前两次使用结合起来的一种方法。
第一次使用 slice
会创建一个从开始到要删除的元素之前的数组。
第二次使用 slice
会创建一个从要删除的元素之后到数组末尾的数组。
使用 concat
将这两个数组连接在一起,形成一个与初始数组相似的数组,但没有特定的元素。
使用 filter 移除具有特定值的元素
如果想删除具有某个值的元素,可以使用 Array.prototype.filter()
。让我们以同样的 arrayOfLetters
为例,创建一个不带 d
的副本。
const arrayOfLetters = ['a', 'b', 'c', 'd', 'e', 'f'];
const arrayWithoutD = arrayOfLetters.filter(function (letter) {
return letter !== 'd';
});
// arrayOfLetters 没有被改变
console.log(arrayOfLetters); // ['a', 'b', 'c', 'd', 'e', 'f']
console.log(arrayWithoutD); // ['a', 'b', 'c', 'e', 'f']
filter
接收一个回调,并用该回调测试数组中的所有元素。它会保留回调返回 true
(或真值)的元素,并排除回调返回 false
(或假值)的元素。
在本例中,回调检查 letter !== "d"
,因此对字母 d
返回 false
,对所有其他元素返回 true
,从而得到一个不包含字母 d
的数组。
filter
回调传递三个参数,依次是元素本身、元素索引和整个数组。
你可以根据需要创建比本例更复杂的条件。
使用 for 循环和 push 从数组中移除元素
在不改变原始数组的情况下从数组中移除元素的最后一种方法是使用 push
方法。
只需以下简单步骤:
- 创建一个空数组
- 遍历原始数组
- 将想要保留的元素推送到空数组中
const arrayOfLetters = ['a', 'b', 'c', 'd', 'e', 'f'];
const arrayWithoutB = [];
for (let i = 0; i < arrayOfLetters.length; i++) {
if (arrayOfLetters[i] !== 'b') {
arrayWithoutH.push(arrayOfLetters[i]);
}
}
// arrayOfLetters 没有被改变
console.log(arrayOfLetters); // ['a', 'b', 'c', 'd', 'e', 'f']
console.log(arrayWithoutB); // ['a', 'c', 'd', 'e', 'f']
对于更复杂的语句,if
语句条件可以同时检查索引(i
)和元素值。
使用解构赋值和 rest 操作符移除数组的第一个元素
数组解构和 rest 操作符是两个有点令人困惑的概念。
如果你想深入了解这个话题,我建议你阅读《JavaScript 数组解构和对象解构》这篇文章,其中介绍了如何对数组进行解构。
你可以使用解构删除第一个元素——比方说删除一个名为 arr
的数组——然后创建一个名为 newArr
的新数组,方法是:const [ , ...newarr] = arr;
。
现在,我们来看一个使用解构和 rest 操作符的实际例子。
const arrayOfFruits = ['olive', 'apricot', 'cherry', 'peach', 'plum', 'mango'];
const [ , ...arrayOfCulinaryFruits] = arrayOfFruits;
// arrayOfFruits 没有被改变
console.log(arrayOfFruits); // ['olive', 'apricot', 'cherry', 'peach', 'plum', 'mango']
console.log(arrayOfCulinaryFruits); // ['apricot', 'cherry', 'peach', 'plum', 'mango']
在 rest 运算符前加逗号表示避开数组中的第一个元素,其他所有元素都被复制到 arrayOfCulinaryFruits
数组中。
如何在改变数组的同时从数组中移除一个元素
在某些情况下,可能需要改变原始数组。在这种情况下,你也可以使用下面的方法。
Array.prototype.pop()
Array.prototype.shift()
Array.prototype.splice()
使用 pop 删除数组的最后一个元素
你可以使用 Array.prototype.pop()
删除数组的最后一个元素。
如果你有一个名为 arr
的数组,则是 arr.pop()
。
const arrayOfNumbers = [1, 2, 3, 4];
const previousLastElementOfTheArray = arrayOfNumbers.pop();
console.log(arrayOfNumbers); // [1, 2, 3]
console.log(previousLastElementOfTheArray); // 4
pop
方法用于数组,它通过删除数组的最后一项来改变数组。
pop 方法还会返回移除的元素。
用 shift 删除数组的第一个元素
可以在数组中使用 shift
方法移除数组的第一个元素。
如果有一个名为 arr
的数组,可以这样使用:arr.shift()
。
const arrayOfNumbers = [1, 2, 3, 4];
const previousFirstElementOfTheArray = arrayOfNumbers.shift();
console.log(arrayOfNumbers); // [2, 3, 4]
console.log(previousFirstElementOfTheArray); // 1
shift
方法移除数组的第一个元素。
它还会返回移除的元素。
使用 splice 删除任意索引处的元素
使用 splice
方法可以删除任意索引处的元素。
如果你有一个名为 arr
的数组,可以使用这种方法移除任意索引处的元素:arr.splice(n, 1)
,其中 n
是要移除元素的索引。
const arrayOfNumbers = [1, 2, 3, 4];
const previousSecondElementOfTheArray = arrayOfNumbers.splice(1, 1);
console.log(arrayOfNumbers); // [1, 3, 4]
console.log(previousSecondElementOfTheArray); // [2]
splice
方法可以接受多个参数。
要移除任意索引处的元素,需要给 splice
两个参数:第一个参数是要移除元素的索引,第二个参数是要移除元素的个数。
因此,如果有一个名为 arr
的数组,要删除索引为 4
的元素,使用 splice
方法的方法是:arr.splice(4, 1)
。
然后,splice
方法会返回一个包含移除元素的数组。
总结
在 JavaScript 中,有许多不同的方法来做同一件事。
在本文中,你已经学会了从数组中移除元素的九种不同方法。其中六种不会改变原始数组,三种会改变原始数组。
你也许会在某个时候用到所有这些方法或者其中一种,也许你还会学到更多方法实现同样的目的。