在 freeCodeCamp 基础数据结构课程中掌握 Array.splice():从数组中间删除元素
导读
Array.prototype.splice() 是 JavaScript 中最强大的数组方法之一,它不仅能从任意位置一次性删除多个连续元素,还能原地修改原数组并返回被移除元素的集合。这篇指南围绕 freeCodeCamp「JavaScript 算法与数据结构」认证下 Basic Data Structures(基础数据结构)模块中的 Remove Items Using splice() 这一实战挑战展开,从零索引原理、参数语义到可运行代码逐层剖析,并结合作战题目自带的测试断言与官方解法,帮助你彻底掌握用 splice() 完成数组“定点批量删除”的实战能力。
挑战概览:为什么需要 splice()?
本挑战文件位于 curriculum/challenges/english/blocks/basic-data-structures/587d78b2367417b2b2512b10.md,在课程体系中的定位可以这样理解:
- 它属于 Basic Data Structures block(模块),其结构定义见 curriculum/structure/blocks/basic-data-structures.json,
challengeOrder中列出了该 block 下全部 20 个挑战的先后顺序; - 该 block 归属于 JavaScript Algorithms and Data Structures 认证的 superblock,superblock 结构文件为 curriculum/structure/superblocks/javascript-algorithms-and-data-structures-22.json 与 curriculum/structure/superblocks/javascript-algorithms-and-data-structures.json。
该挑战的前置铺垫是 Remove Items from an Array with pop() and shift(),后续衔接是 Add Items Using splice()。
在学完 push() / unshift()(从尾/头部添加)和 pop() / shift()(从尾/头部删除)之后,会出现一个明显的能力缺口:
pop()只能删掉数组末尾的一个元素;shift()只能删掉数组开头的一个元素;- 当你想删掉数组中间某个位置的元素,或者想一次删除多个元素时,这两个方法都无能为力。
这正是 splice() 的用武之地。如挑战 description 中所述:
splice()allows us to do just that: remove any number of consecutive elements from anywhere in an array.
即:splice() 可以从数组的任意位置移除任意数量的连续元素。
参数语义与前两个参数详解
挑战原文指出,splice() 最多可以接收 3 个参数,但本挑战先聚焦前两个。
array.splice(start, deleteCount);
第一个参数 start:开始删除的索引
- 它是一个整数,代表调用
splice()的数组上的索引(位置)。 - JavaScript 数组是**从零开始索引(zero-indexed)**的,所以要指向数组的第一个元素必须写
0。这一点挑战原文明确强调:“arrays are zero-indexed, so to indicate the first element of an array, we would use0”。 start表示从此索引处开始删除元素。
第二个参数 deleteCount:要删除的元素个数
- 它也是一个整数,代表从
start开始向后数、一共删除多少个元素。
综合起来:array.splice(start, deleteCount) 的含义是“从索引 start 开始,向后删除 deleteCount 个连续元素”。
逐步演示:从中间删除
挑战给出的入门例子:
let array = ['today', 'was', 'not', 'so', 'great'];
array.splice(2, 2);
array.splice(2, 2) 的解析过程:
| 索引 | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| 原数组元素 | 'today' |
'was' |
'not' |
'so' |
'great' |
- 第一个参数
2:从索引 2 开始,对应元素'not'; - 第二个参数
2:连续删除 2 个元素,即'not'和'so'(索引 2、3)。
操作结束后 array 变成 ['today', 'was', 'great'],与挑战原文结论完全一致。可以看到,删除后剩余元素会自动前移填补空缺,数组长度由 5 变为 3。
splice() 是原地修改并返回被删元素的数组
splice() 的行为与 slice() 有一个极其容易混淆的关键区别:它会原地(in place)修改调用它的数组本身,且其返回值是一个包含所有被删除元素的新数组,而非修改后的原数组。
挑战原文示例:
let array = ['I', 'am', 'feeling', 'really', 'happy'];
let newArray = array.splice(3, 2);
array是['I', 'am', 'feeling', 'really', 'happy'];array.splice(3, 2):从索引 3('really')开始删 2 个元素,即'really'与'happy';- 原数组
array被原地修改为['I', 'am', 'feeling']; - 返回值
newArray为['really', 'happy']。
这里体现了“一鱼两吃”的典型用法:既要保留原数组删除后的结果,又要拿到被删元素去做后续处理。
与 slice() 的对比记忆点
同为数组截取工具,slice(start, end) 不修改原数组并返回副本;而 splice(start, deleteCount) 原地修改原数组并把删除部分作为返回值。凡是看到“原地删除 + 取出被删项”的需求,都应优先考虑 splice()。
本挑战的实战任务:让元素之和等于 10
题目要求(instructions)
We've initialized an array
arr. Usesplice()to remove elements fromarr, so that it only contains elements that sum to the value of10.
即:使用 splice() 从数组 arr 中删除若干元素,使 arr 剩下的元素之和恰好等于 10。
起始代码(seed)
题目给出的可运行起始代码(位于该 md 文件的 # --seed-- / --seed-contents-- 段):
const arr = [2, 4, 5, 1, 7, 5, 2, 1];
// Only change code below this line
// Only change code above this line
console.log(arr);
在 freeCodeCamp 中,--seed-contents-- 定义了学习者编辑区中预先填充的代码,用户只能在两行注释之间修改。
解题推理
先计算数组元素的总和:
2 + 4 + 5 + 1 + 7 + 5 + 2 + 1 = 27
目标是剩下的元素和为 10,因此需要删掉的元素之和为:
27 − 10 = 17
观察数组 [2, 4, 5, 1, 7, 5, 2, 1],索引排列如下:
| 索引 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|---|---|
| 值 | 2 | 4 | 5 | 1 | 7 | 5 | 2 | 1 |
连续片段 4 + 5 + 1 + 7 = 17,它们恰好位于索引 1 到 4,是一段连续元素,非常适合用 splice() 一次性删除。删除后剩余 [2, 5, 2, 1],和为 2 + 5 + 2 + 1 = 10,满足题目要求。
官方参考解法(--solutions--)
const arr = [2, 4, 5, 1, 7, 5, 2, 1];
arr.splice(1, 4);
- 第一个参数
1:从索引 1(值4)开始; - 第二个参数
4:连续删除 4 个元素4, 5, 1, 7; - 原数组被原地修改为
[2, 5, 2, 1],元素之和为10。
值得注意的是,该挑战规定只能删除、不能新增元素。如果传入第三个及以上参数,例如 arr.splice(1, 4, 0),虽然同样能得到和 10,却会违反“只删不加”的约束,被下方的测试判定为错误。
逐条拆解测试断言(hints)背后的校验逻辑
该挑战在 md 文件的 # --hints-- 段内置了 4 条自动化断言。freeCodeCamp 的挑战测试使用一个代码归一化工具链:在断言正则前先调用 __helpers.removeJSComments(code) 去除代码中的注释,再用 __helpers.removeWhiteSpace(...) 去掉空白字符,避免因缩进、换行或注释干扰匹配。
断言一:不得改动初始化的数组声明行
assert(
__helpers.removeWhiteSpace(__helpers.removeJSComments(code)).match(/constarr=\[2,4,5,1,7,5,2,1\];?/)
);
要求代码中必须保留原始声明 const arr = [2, 4, 5, 1, 7, 5, 2, 1];(去除空白与注释后形如 constarr=[2,4,5,1,7,5,2,1];),即不能偷改数据源。
断言二:剩余元素之和必须严格等于 10
assert.strictEqual(
arr.reduce((a, b) => a + b),
10
);
使用 arr.reduce((a, b) => a + b) 累加全部剩余元素,并用 strictEqual 严格等于 10。任何和不为 10 的删除方案都会被拒绝。
断言三:必须调用 splice() 方法
assert(__helpers.removeWhiteSpace(__helpers.removeJSComments(code)).match(/arr\.splice\(/));
检查用户代码中是否出现了 arr.splice( 的调用,强制要求使用 splice() 而不是其他手段(例如手动重建数组、使用 filter() 等)。
断言四:splice 只能删除,不能新增元素
assert(
!__helpers.removeWhiteSpace(__helpers.removeJSComments(code)).match(/arr\.splice\(\d+,\d+,\d+.*\)/g)
);
这是本挑战最容易踩坑的约束:正则 arr\.splice\(\d+,\d+,\d+.*\) 匹配“出现了第三个(数字)参数及之后内容”的调用形式(即 arr.splice(start, deleteCount, item1, ...))。外层 ! 取反,意味着只要检测到添加元素的调用,测试即失败。这从侧面印证了课程的知识点划分:删除只用前两个参数,第三个参数的“边删边加”能力被留给了下一个挑战 Add Items Using splice() 去专门讲解。
方法脉络:从 pop/shift 到 splice 的完整删除工具箱
在 Basic Data Structures 模块中,删除类 API 的教学顺序是有意设计的:
| 方法 | 作用位置 | 删除数量 | 是否修改原数组 | 返回值 | 对应挑战 |
|---|---|---|---|---|---|
pop() |
数组末尾 | 每次 1 个 | 是 | 被删除的元素本身 | Remove Items from an Array with pop() and shift() |
shift() |
数组开头 | 每次 1 个 | 是 | 被删除的元素本身 | 同上 |
splice(start, deleteCount) |
任意位置 | 任意个连续元素 | 是 | 由被删元素组成的新数组 | 本文所在挑战 |
splice(start, deleteCount, ...items) |
任意位置 | 任意个连续元素,同时插入新元素 | 是 | 由被删元素组成的新数组 | Add Items Using splice() |
如挑战原文所述,pop() 与 shift() 的共同弱点是“each only allows an array to be modified by a single element at a time”(每次只能修改单个元素),并且只能作用于两端。而 splice() 把“位置选择”和“批量删除”两个能力合二为一,是数组删除场景中最具通用性的方法。
splice() 的通用行为速查(超出本挑战的延伸知识)
虽然本挑战只用到前两个参数与“纯删除”场景,理解以下几点有助于在真实项目中正确使用:
- 原地修改:
splice()直接改写调用它的数组,如需保留副本应先const copy = [...arr]或用slice()复制; - 返回值语义:返回被删除元素构成的新数组;若
deleteCount为 0,则返回空数组([]); - 删除到末尾的兜底:若
deleteCount超过start之后剩余的元素个数,splice()会删到数组末尾为止; - 负索引支持:
start可以为负,-1指向最后一个元素(与许多现代数组方法的约定一致); - deleteCount 为 0:此时
splice(start, 0)不删除任何元素——配合第三个参数即可在指定索引处做“纯插入”,这一能力是下一个挑战的伏笔。
在 freeCodeCamp 平台上如何运行与验证
本挑战属于 challengeType: 1(标准代码题,对应挑战文件 front matter 中的 challengeType: 1)。你可以:
- 在 freeCodeCamp 官网「JavaScript Algorithms and Data Structures」认证的 Basic Data Structures 模块中找到 “Remove Items Using splice()” 挑战;
- 将种子代码(
--seed-contents--内容)粘贴到浏览器内置编辑器中,在 “Only change code below this line” 与 “Only change code above this line” 之间补写arr.splice(1, 4);; - 点击运行,观察
console.log(arr)是否输出[2, 5, 2, 1]; - 提交后由
# --hints--中的 4 条断言自动评分,全部通过即完成挑战。
若想在本地复现逻辑,可在任意 Node.js 环境直接执行:
const arr = [2, 4, 5, 1, 7, 5, 2, 1];
arr.splice(1, 4);
console.log(arr); // [2, 5, 2, 1]
console.log(arr.reduce((a, b) => a + b)); // 10
在仓库层面,该挑战的完整定义(描述、指令、断言、种子代码、官方解法)都收敛在 curriculum/challenges/english/blocks/basic-data-structures/587d78b2367417b2b2512b10.md 这一个 Markdown 文件中,这也体现了 freeCodeCamp 课程“单文件自包含、前后挑战按 block 顺序串联”的内容组织方式。
小结
Remove Items Using splice() 这一挑战解决的核心问题是:如何在任意位置批量删除连续元素。它补齐了 pop()/shift() 只能“两端单删”的短板,同时通过“只能删、不能加”的测试约束,把第三个参数的教学任务精准地让位给下一个挑战,体现出课程设计层层递进的特点。掌握 splice(start, deleteCount) 的两个核心参数、零索引定位方式与“返回被删元素数组 + 原地修改原数组”的双重语义,你就能在实际编码中熟练地完成数组的定点清洗与元素抽取。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00