☰
30 seconds of code:从 JavaScript 数组头部或尾部移除 n 个元素的完整指南
2026/10/4 22:32:40 网站建设 项目流程
  • 教程
  • 文档

【免费下载链接】30-seconds-of-code

Coding articles to level up your development skills

项目地址:https://gitcode.com/gh_mirrors/30/30-seconds-of-code
点击查看免费下载

导读

在 JavaScript 日常开发中,数组元素的删除是出现频率极高的操作,而"从数组的开头或结尾移除若干个元素"更是其中最常见、也最容易踩坑的场景。本文以 30 seconds of code 仓库中的 remove-first-last-n-array-elements.md 为骨架,系统讲解基于Array.prototype.slice()、splice()、shift()、pop()、findIndex()与findLastIndex()的六种删减方案,并深入剖析各 API 的边界行为与不可变编程(immutability)取舍。读完本文,你将能够针对"移除前/后 n 个元素"以及"按条件移除头部/尾部元素"两类需求,直接写出健壮、可复用的一行式工具函数。

从"删除单个元素"说起:shift()与pop()

在讨论批量移除之前,先回顾最基础的两个可变方法(mutative methods):

  • Array.prototype.shift():移除数组第一个元素,返回被移除的元素,原地修改数组。
  • Array.prototype.pop():移除数组最后一个元素,返回被移除的元素,原地修改数组。

两者共同特点是:会改变原数组,且返回值是被删除的元素(而非删除后的数组)。它们只能一次删除一个元素。

const arr = ['a', 'b', 'c']; const first = arr.shift(); // 'a' const last = arr.pop(); // 'c' console.log(arr); // ['b']

可以看到,arr在执行完shift()和pop()后只剩下['b']。这种副作用在某些场景(例如状态管理、纯函数编程)中是不可接受的,因此文章核心思路是:优先使用不修改原数组的Array.prototype.slice()。

批量移除的核心工具:splice()与slice()

当需要移除多个元素时,有两个主力方法:

方法是否修改原数组返回值适用场景
Array.prototype.splice()修改(变异)被删除的元素组成的数组明确需要原地删除,且关心被删元素
Array.prototype.slice()不修改新的数组(原数组浅拷贝的子集)希望保持原数组不变,获得新数组

Array.prototype.splice(start, deleteCount)是删除元素的"通用工具",但它会变异原数组;而Array.prototype.slice(begin, end)则基于原数组返回一个全新的数组,原数组保持不变。

注:本文重点讲解slice()路线,因为"变异原数组通常不是我们想要的结果"(原文语)。如需不修改原数组的splice()替代实现,仓库中有专门文章 remove-elements-from-array,其核心思路是用slice()+concat()组合出一个shank()函数。

从头部移除 n 个元素:slice(n)

使用slice()时,传入正数起始索引、不传结束索引即可:返回的新数组从第n个元素(索引n)开始,天然跳过前n个元素。

const drop = (arr, n = 1) => arr.slice(n); drop([1, 2, 3]); // [2, 3] drop([1, 2, 3], 2); // [3] drop([1, 2, 3], 42); // []

注意两个细节:

  • 默认参数n = 1让"只移一个"成为默认行为;
  • 当n超过数组长度时(如42),slice()会安全地返回空数组[],不会抛错。

从尾部移除 n 个元素:slice(0, -n)

从尾部移除则需要负数的结束索引:slice(0, -n)表示"取从索引 0 开始、到倒数第n个元素为止(不含)"的新数组,从而把最后n个元素剔除。

const dropLast = (arr, n = 1) => arr.slice(0, -n); dropLast([1, 2, 3]); // [1, 2] dropLast([1, 2, 3], 2); // [1] dropLast([1, 2, 3], 42); // []

同样地,n过大时slice()返回[],行为是安全的。

边界提醒:slice()对负数与越界索引有非常宽容的处理逻辑。传入0作为n(如slice(0, 0))会得到空数组,而传入负的n则会产生与直觉相反的结果。仓库姊妹文章 first-last-n-elements 与 take-n-elements-from-array-start-or-end 对此有更深入的讨论,必要时可对n做n >= 0校验或使用三元表达式兜底。

按条件移除:从"删除固定数量"到"删除直到条件成立"

Array.prototype.filter()天然适合"删除所有满足条件的元素",但它必须遍历整个数组;当我们需要"从头部开始,删除所有元素直到遇到第一个满足条件的元素为止"(即"删除前缀")时,filter()就显得笨重且低效。

更优的思路是两步走:

  1. 用findIndex()/findLastIndex()定位第一个/最后一个满足条件的元素;
  2. 用slice()切出需要的部分。

按条件移除头部元素:findIndex()+slice()

const dropUntil = (arr, func) => { const index = arr.findIndex(func); return arr.slice(index >= 0 ? index : arr.length); } dropUntil([1, 2, 3, 4], n => n >= 3); // [3, 4]

执行流程分析:

  • findIndex(func)返回数组中第一个使func为真的元素索引;
  • 如果找到(index >= 0),从该索引开始切片,前缀被丢弃;
  • 如果没有找到(findIndex返回-1),则回退到arr.length——slice(arr.length)得到空数组[],语义为"所有元素都被丢弃"。

对[1, 2, 3, 4]与条件n => n >= 3,第一个满足条件的元素是3(索引 2),于是返回[3, 4]。

按条件移除尾部元素:findLastIndex()+slice()

对称地,移除尾部满足条件的元素需要定位最后一个满足条件的元素。Array.prototype.findLastIndex()是从数组末尾向前搜索的现代方法(与findIndex()互补):

const dropLastUntil = (arr, func) => { const index = arr.findLastIndex(func); return arr.slice(0, index >= 0 ? index + 1 : arr.length); } dropLastUntil([1, 2, 3, 4], n => n < 3); // [1, 2]

这里的关键差异在于切片结束索引的计算:

  • 找到最后一个满足条件的元素索引index后,需要保留包含该元素在内的前缀,因此结束索引取index + 1;
  • 若找不到(-1),则回退为arr.length,返回整个数组(语义为"一个都不删")。

对[1, 2, 3, 4]与条件n => n < 3,最后一个小于 3 的元素是2(索引 1),slice(0, 2)得到[1, 2]。

六种方案速查表与取舍建议

需求工具函数是否变异原数组备注
移除头部 1 个arr.shift()是返回被删元素
移除尾部 1 个arr.pop()是返回被删元素
移除头部 n 个drop(arr, n) => arr.slice(n)否n越界安全返回[]
移除尾部 n 个dropLast(arr, n) => arr.slice(0, -n)否n越界安全返回[]
按条件移除头部dropUntil(arr, fn)用findIndex+slice否找不到时返回[]
按条件移除尾部dropLastUntil(arr, fn)用findLastIndex+slice否找不到时返回原数组

选择建议:

  • 追求性能且不关心原数组→shift()/pop()/splice();
  • 追求不可变性(immutability)→slice()系列,这也是原文重点强调的默认选择;
  • 需要按条件批量删除 →findIndex()/findLastIndex()定位 +slice()切片;
  • 需要删除任意位置/全部匹配的元素 → 参考仓库中的 remove-element-from-array、remove-elements-from-array 与 remove-matching-array-elements 三篇文章。

从源码看这些技巧在项目中的位置

本文的六个工具函数(drop、dropLast、dropUntil、dropLastUntil)在 30 seconds of code 中属于JavaScript 数组(array)主题的经典技巧,与以下仓库资源同属一个知识簇:

  • first-last-n-elements:取前/后 n 个元素(slice(0, n)/slice(-n)),与本文的"删除"互为镜像;
  • take-n-elements-from-array-start-or-end:深入讨论了n = 0、n越界、负值等slice()边界行为;
  • take-elements-by-condition-from-array-start-or-end:按条件保留前缀/后缀的takeWhile/takeRightWhile,与本文dropUntil/dropLastUntil形成"保/删"对照;
  • array-head-tail:数组head/tail的基础概念,同样基于slice(1)实现;
  • fast-remove-array-element:对无序大数组用"拷贝末位 +pop()"的提速技巧。

这些文章统一使用Array.prototype.slice()作为核心原语,体现了 30 seconds of code 一以贯之的"短小、无依赖、可直接复用"的风格;本文的函数体均可在项目中以单行/数行的形式直接落地。

结语

处理数组头尾元素的移除,核心是两对选择:可变 vs 不可变(shift()/pop()/splice()vsslice()),以及固定数量 vs 按条件(slice(n)vsfindIndex/findLastIndex+slice)。掌握slice()对负数索引与越界索引的宽容语义,再配合findIndex()/findLastIndex()的定位能力,你就能用几行代码优雅地覆盖从"删一个"到"删到条件成立"的全部场景,同时保持原数组不变、避免不必要的性能损耗。

  • 教程
  • 文档

【免费下载链接】30-seconds-of-code

Coding articles to level up your development skills

项目地址:https://gitcode.com/gh_mirrors/30/30-seconds-of-code
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询