前端进阶之旅前端进阶之旅
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
首页程序员面试题库for 循环数组边界 off-by-one
JSJavaScript数组与集合

遍历数组时为什么通常写 i < length,怎样避免多一次或少一次?

先写清楚合法索引集合,再选择比较符号;不要只记住一个 length 减一的口号。

前端进阶之旅 · 一题精讲更新于 2026.09.06
JavaScript#数组与集合
先看核心答案读代码示例
理解线索

用三个条件描述扫描范围

  1. 从哪里开始通常为零,子区间可从明确起点开始
  2. 在哪里停止在不包含的终点之前继续
  3. 怎样推进每次步进必须朝终止条件靠近

越界读取普通数组常得到 undefined 而不抛错,因此错误可能表现为 NaN、空行或遗漏数据,而不是立即崩溃。

核心回答

先记住这个答案

普通数组长度为 n 时,合法位置通常是零到 n 减一,所以正向扫描常写 for (let i = 0; i < array.length; i++),对应半开区间 [0, n)。写 i <= length 会多读越界位置,写 i < length - 1 会遗漏末项;i <= length - 1 在长度不变且整数索引的前提下也可以正确。真正要避免的是起点、终点包含规则和步进不一致。空数组、单元素和末项有特殊值的测试最容易暴露偏一错误,循环中修改长度则需要另外定义扫描范围。

  • 零基索引的尾部边界不包含 length
  • 小于 length减一漏末项,小于等于 length 多一项
  • 半开区间便于表达空区间与片段拼接

把正常、遗漏和越界放在同一输入上

示例选择最后一项明显不同的数组,正确循环保留全部值,错误的小于 length 减一漏掉末项。越界版本不直接把 undefined 序列化,而是明确标记,避免 JSON 的 null 转换掩盖问题。

测试空数组与单元素尤其有效:空数组不该进入循环,单元素应恰好处理一次。只用多元素且值相同的输入,遗漏或重复处理可能不容易被观察到。

比较三种终止条件JavaScript
const values = ['first', 'last'];
const correct = [];
for (let i = 0; i < values.length; i++) correct.push(values[i]);
const missed = [];
for (let i = 0; i < values.length - 1; i++) missed.push(values[i]);
const extra = [];
for (let i = 0; i <= values.length; i++) extra.push(values[i] === undefined ? 'out-of-range' : values[i]);
console.log(JSON.stringify(correct));
console.log(JSON.stringify(missed));
console.log(JSON.stringify(extra));
查看输出与解释
["first","last"]
["first"]
["first","last","out-of-range"]

错误来自比较符号与终点定义不匹配,而不是 length 减一这个表达式本身非法。示例输入不含真实 undefined,才能把最后标记明确归因于额外读取。

子区间同样适合用不包含终点表示

处理索引 start 到 end 之前的项目时,循环条件 i < end 与 slice 的区间直觉容易对齐。相邻片段可以使用前一段终点作为后一段起点,减少重叠或缺口。

边界若来自用户参数,应先校验整数、范围和起止关系,不能依赖 JavaScript 隐式转换来修复。对于允许空区间的接口,start 等于 end 应有明确结果,而不是被误判成必须再处理一项。

长度变化会让看似正确的条件改变意义

每轮读取 array.length 的循环可能看到后来追加的元素;若回调持续追加,就可能无法结束。缓存初始长度只固定扫描上界,不会冻结未读取元素的值,也不会阻止删除导致的缺口。

正向遍历中 splice 删除当前项会让后续元素左移,索引继续增加可能跳过一项。应根据需求选择倒序删除、构造新结果或明确调整索引,不要在一个循环里混用多种未说明的结构修改。

回答前,多想一步

容易答错的地方

把 i 小于等于 length减一一概判成错误
在长度稳定、索引按整数递增的条件下它与 i 小于 length 等价。代码评审应检查真实边界契约,而不是机械禁止某个表达式;半开写法的优势主要是更容易组合和理解。
把缓存 length 当成完整快照
它只固定上界,数组元素仍可能被修改或删除,索引存在性也会变化。需要处理固定批次时应明确复制或版本策略,不能用一个局部数字代替数据一致性设计。
试着用自己的话回答

面试官还会怎么问?

倒序遍历的终止条件怎么写?

常从 length 减一开始,在 i 大于等于零时继续并每轮递减。空数组起点为负一,循环自然不执行;若要倒序处理子区间,应重新写清包含起点和终点规则再推导。

为什么多读一次容易得到 NaN?

额外位置常读到 undefined,参与普通数值运算后可能传播为 NaN。应首先检查合法索引和输入类型,而不是在最终结果处把 NaN 强行替换成零,避免掩盖真正的循环错误。

什么时候改成 for...of 更清楚?

只关心值且不需要复杂索引边界时,for...of 可以减少手写索引错误。若处理相邻位置、指定子区间或原地修改,显式索引仍有价值,选择应围绕算法实际需要的信息。

从一道题,走向一组知识

把知识连起来

数组与集合

为什么空数组 every 返回 true,而 some 返回 false?

用空输入理解集合条件与回调次数的边界。

数组与集合

数组 keys、values、entries 返回什么,为什么遍历第二次没有数据?

需要同时取得索引和值时使用明确的 entries 视角。

参考资料

  • MDN:循环与迭代
  • MDN:Array

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 把正常、遗漏和越界放在同一输入上
  3. 子区间同样适合用不包含终点表示
  4. 长度变化会让看似正确的条件改变意义
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑