资讯动态

JavaScript 数组原地区间过滤:实现 filterRangeInPlace 与 splice 的实战演练

发布时间:2026/10/3 8:38:43 来源:尧图企业网站定制
文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载导读filterRangeInPlace(arr, a, b)是 Modern JavaScript Tutorial 中 JavaScript 数组方法章节1-js/05-data-types/05-array-methods/的一道经典编程任务它要求在不创建新数组的前提下从原数组中删除所有不在闭区间[a, b]内的元素。它与姊妹任务filterRange形成鲜明对比——一个修改原数组、一个返回新数组两者共同帮你吃透“原地修改in-place mutation”与“纯函数式过滤”两种编程范式的边界。阅读本文后你将掌握用Array.prototype.splice安全删除数组元素的完整套路含索引回退技巧并能对照仓库中的官方测试用例验证你的实现是否正确。任务原文与核心要求任务定义位于 3-filter-range-in-place/task.md原文要点如下Write a functionfilterRangeInPlace(arr, a, b)that gets an arrayarrand removes from it all values except those that are betweenaandb. The test is:a ≤ arr[i] ≤ b. The function should only modify the array. It should not return anything.任务示例保留区间[1, 4]内的值let arr [5, 3, 8, 1]; filterRangeInPlace(arr, 1, 4); // removed the numbers except from 1 to 4 alert( arr ); // [3, 1]需要特别注意的是三个硬性约束保留条件a ≤ arr[i] ≤ b即闭区间边界值本身也要保留原地修改必须直接操作传入的数组arr不得新建数组再重新赋值无返回值函数体内不能return任何值返回undefined调用方只能通过原数组的引用观察结果。这三条约束与姊妹任务filterRange见 2-filter-range/task.md完全相反——filterRange明确要求should not modify the array并返回新数组。两个任务放在一起正是一组刻意设计的对照实验同一个过滤语义分别用返回新数组和原地删除两种方式实现帮助读者建立哪些数组操作会产生副本、哪些会原地生效的深刻认知。官方参考解法splice 索引回退仓库在 solution.md 给出了官方参考实现可运行的独立版本位于 _js.view/solution.jsfunction filterRangeInPlace(arr, a, b) { for (let i 0; i arr.length; i) { let val arr[i]; // remove if outside of the interval if (val a || val b) { arr.splice(i, 1); i--; } } } let arr [5, 3, 8, 1]; filterRangeInPlace(arr, 1, 4); // removed the numbers except from 1 to 4 alert( arr ); // [3, 1]逐行拆解for (let i 0; i arr.length; i)正向遍历数组。注意循环条件每次都会重新读取arr.length这在下文删除场景中是关键let val arr[i];缓存当前元素避免重复取值if (val a || val b)判定是否在区间之外——val a或val b任一成立即删除。这里巧妙地用“排除法”实现区间过滤与任务要求的保留条件a ≤ arr[i] ≤ b逻辑等价arr.splice(i, 1);从索引i处删除 1 个元素后续元素全部前移一位i--;索引回退。删除后原位于i1的元素移动到了i的位置如果不把i减回去下一次循环就会跳过这个被“挤”过来的元素。为什么必须i--模拟运行以arr [5, 3, 8, 1]、区间[1, 4]为例逐步推演步骤i数组状态当前值判定操作后数组10[5, 3, 8, 1]55 4删除[3, 8, 1]20i--后回到 0循环再i[3, 8, 1]31 ≤ 3 ≤ 4保留[3, 8, 1]31[3, 8, 1]88 4删除[3, 1]41回退后[3, 1]11 ≤ 1 ≤ 4保留[3, 1]52[3, 1]—i arr.length为假循环结束[3, 1]如果去掉i--删除 5 后索引跳到 1直接检查原索引 2 处的元素 8——数组此时是[3, 8, 1]索引 1 处是 88 会被删除但紧随其后的逻辑会跳过紧邻的下一个元素。更致命的是若连续多个元素需要删除例如[6, 7, 8]配区间[1, 4]删除 6 后索引跳到 1此时索引 1 处已是 7随后 8 却因索引跳跃被漏掉。这就是经典的“删除元素后索引前移”陷阱。为什么不用delete arr[i]教程正文 article.md 专门对比了delete与splicelet arr [I, go, home]; delete arr[1]; // remove go alert( arr[1] ); // undefined // now arr [I, , home]; alert( arr.length ); // 3delete arr[i]只会把该位置的值清为undefined数组长度不变后面的元素也不会前移——它留下了空洞。任务要求“remove from the array”期望的是数组变短、元素紧凑这正是 arr.splice 的职责它以arr.splice(start, deleteCount, elem1, ..., elemN)的形式原地删除、插入或替换元素并返回被删除元素的数组。官方测试用例验证三种约束仓库在 _js.view/test.js 中提供了基于 Mocha Chai 的完整测试套件恰好逐条验证了任务的三个硬性约束describe(filterRangeInPlace, function() { it(returns the filtered values, function() { let arr [5, 3, 8, 1]; filterRangeInPlace(arr, 2, 5); assert.deepEqual(arr, [5, 3]); }); it(doesnt return anything, function() { assert.isUndefined(filterRangeInPlace([1,2,3], 1, 4)); }); });两个用例的用意值得逐条分析第一个用例assert.deepEqual(arr, [5, 3])传入[5, 3, 8, 1]与区间[2, 5]期望原数组被原地修改为[5, 3]。注意这里区间是[2, 5]而任务示例用的是[1, 4]——测试刻意更换了区间验证函数对任意a、b都成立。assert.deepEqual用于深度比较数组内容若函数返回了新数组而没改原数组此断言必然失败第二个用例assert.isUndefined(...)直接断言函数返回值是undefined即“不返回任何东西”。这条用例对没有return的函数天然通过若实现中不小心写了return arr或return filteredArray测试就会亮红灯。这套测试可配合仓库中其他任务的测试规范如 3-pow-test-wrong 对 Mocha 用法的讲解理解describe/it组织用例、assert断言结果正是 Modern JavaScript Tutorial 中“测试驱动开发”章节贯穿始终的实践方式。与 filterRange 的对照副本 vs 原地理解本题最佳路径是对照阅读姊妹任务 filterRange。该任务要求let arr [5, 3, 8, 1]; let filtered filterRange(arr, 1, 4); alert( filtered ); // 3,1 (matching values) alert( arr ); // 5,3,8,1 (not modified)两个任务对同一份数据、同一个区间语义a ≤ x ≤ b给出截然相反的 API 设计维度filterRange返回新数组filterRangeInPlace原地修改是否修改原数组否原数组保持不变是原数组被直接改写返回值新数组过滤结果undefined不返回典型实现手段arr.filterfor循环 arr.splice适用场景保留原始数据、链式调用省内存、不需要保留原数组filterRange的经典一行实现基于教程正文讲解的 arr.filter(fn)function filterRange(arr, a, b) { return arr.filter(item (a item item b)); }filter会为每个元素调用回调返回所有让回调为true的元素组成的新数组且绝不触碰原数组——这与splice的原地语义形成教科书级对比。教程正文的总结也印证了这一点见 article.md 附近sort、reverse和splice会修改数组本身而filter、map、slice等返回新副本。边界情况与扩展思考边界情形区间外元素集中在开头如[9, 8, 7]配[1, 4]每次删除后i--回退循环会反复在索引 0 处删除直到遇到区间内元素——i--保证不遗漏任何一个被挤过来的元素空区间 / 无匹配如filterRangeInPlace([1, 2], 5, 9)所有元素都在区间外最终数组被清空为[]若数组本就为空for循环体一次都不执行边界值本身a、b是闭区间端点arr[i] a或arr[i] b必须保留条件里用的是、而非、这一点测试用例保留 5 和 3端点 2、5 均在列可以印证。为什么不在循环里用arr.length快照如果写成let len arr.length提前缓存长度删除元素后len不会变循环会多遍历几个已经不存在的索引虽然访问到undefined不会崩溃undefined a为假不会被误删但会做无谓的迭代。教程解法直接在条件里读取arr.length删除后长度自然缩短循环自动提前结束——简洁且正确。时间复杂度视角每次splice(i, 1)都是 O(n) 操作后续元素整体前移最坏情况下全部元素需要删除总复杂度为 O(n²)。对于追求性能的场景更优的原地做法是用“双指针”先压缩保留元素再裁剪长度例如function filterRangeInPlaceFast(arr, a, b) { let write 0; for (let read 0; read arr.length; read) { if (arr[read] a arr[read] b) { arr[write] arr[read]; } } arr.length write; }这种写法把时间复杂度降到 O(n)。但需要明确这是教程之外的延伸优化官方参考解法采用splice是为了最直观地展示“删除元素”的语义与索引处理技巧。初学阶段建议先吃透官方解法再考虑优化。总结filterRangeInPlace虽然只是 数组方法章节 中的一个练习但它浓缩了 JavaScript 数组操作的三组关键知识splice的原地删除语义它不同于delete的“留空洞”会真正缩短数组并前移元素索引回退技巧正向遍历中删除元素后必须i--否则会跳过紧邻元素——这是数组原地删除类算法通用的反直觉陷阱“修改 vs 副本”的 API 设计意识与filterRange对照理解何时该返回新数组、何时该原地修改以及相应的返回值约定undefined。动手验证方式将 solution.js 的代码粘贴到浏览器控制台或 Node.js 中运行配合 _js.view/test.js 的断言例如把任务示例的区间从[1, 4]换成[2, 5]即可完整复现官方测试的通过流程。建议进一步阅读 数组方法教程正文 中splice、filter、slice的完整讲解把这三个方法的行为边界彻底理清。赞分享文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载相关推荐json-render 中 Remotion Composition 的定义与动态元数据从 Root.tsx 到 Timeline Specjson render 中 Remotion Composition 的定义与动态元数据从 Root.tsx 到 Timeline Spec 本文围绕 ski文档/教程前端freeCodeCamp Slice and Splice 挑战解析用 slice 与 splice 实现不可变数组合并freeCodeCamp Slice and Splice 挑战解析用 slice 与 splice 实现不可变数组合并 本文基于 freeCodeCamp前端后端教育Ghidra BSim 过滤机制详解服务器端与客户端过滤器的工作原理与实战演练Ghidra BSim 过滤机制详解服务器端与客户端过滤器的工作原理与实战演练 本篇技术指南聚焦 Ghidra BSim二进制相似性匹配功能的过滤器体系逆向工程网络安全上一篇LeetCode-Go 题解226. Invert Binary Tree 翻转二叉树的 Go 递归实现与测试详解下一篇Composio DigitalOcean 工具包认证完全指南托管 OAuth2、自定义 OAuth2 与 Personal Access Token 三种接入方式创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑