前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库数组 keys values entries 迭代器区别
JavaScript数组与集合

数组的 keys、values、entries 返回什么?为什么第二次遍历就没数据了?

三个方法都返回数组迭代器:keys() 产出数字下标,values() 产出元素,entries() 产出 [下标, 值]。迭代器是一次性的游标,读到末尾就不会回头,想再遍历一次要重新调用方法拿新的迭代器。

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

三个方法分别产出什么

  1. keys()数字下标 0 到 length - 1,空位也算
  2. values()每个下标上的值,空位读出 undefined,也是 for...of 默认用的迭代器
  3. entries()[下标, 值] 二元数组,配合解构同时拿到两者

迭代器不会在创建时复制数组。创建之后再 push 的元素,只要还没遍历到,照样会被读出来。

核心回答

先记住这个答案

keys()、values()、entries() 返回的都是数组迭代器,分别逐个产出下标、值和 [下标, 值] 二元数组。迭代器不是数组,可以用 for...of、展开语法或者手动调用 next() 来读取,但它没有 length,也没有 map、filter。它内部有一个游标,每读一项往前走一步,走到末尾后就一直是结束状态,所以同一个迭代器第二次展开得到的是空数组。要重新遍历,就再调用一次方法拿一个新的迭代器。

  • 返回的是迭代器,不是数组,没有 map、length
  • 迭代器读完就空了,重新遍历要再调用一次
  • 迭代器是惰性的,遍历时才去读数组的当前内容
  • arr.keys() 包含空位下标,Object.keys 不包含

返回值和「只能用一次」

先把三个方法的产出和重复消费的结果打印出来。

三种迭代器和第二次展开JavaScript
const arr = ['a', 'b']
console.log(JSON.stringify([...arr.keys()]))
console.log(JSON.stringify([...arr.values()]))
console.log(JSON.stringify([...arr.entries()]))

const it = arr.entries()
console.log(Array.isArray(it), typeof it.next) // 不是数组,但有 next 方法
console.log([...it].length, [...it].length) // 第二次已经读完了
查看输出与解释
[0,1]
["a","b"]
[[0,"a"],[1,"b"]]
false function
2 0

迭代器可以想成书签。第一次展开从头读到尾,书签停在最后一页;第二次展开从书签的位置接着读,后面已经没有内容了。每次调用 arr.entries() 都会给你一个新书签,放在第一页。 这个特性在把迭代器存进变量后最容易踩坑。比如先用 [...it] 打日志调试,后面的业务逻辑再用 it 就什么都拿不到了。

迭代器是惰性的

迭代器创建时不会复制数组,它只记住「当前数组」和「读到第几个了」。每次调用 next(),才去数组上读当前下标的值,并和当时的 length 比较决定是否结束。

创建后修改数组,以及稀疏数组JavaScript
const list = [1, 2]
const it = list.values()
list.push(3) // 迭代器创建之后才加的元素
console.log(JSON.stringify([...it]))

const sparse = ['a', , 'c'] // 下标 1 是空位
console.log(JSON.stringify([...sparse.keys()]), JSON.stringify(Object.keys(sparse)))
查看输出与解释
[1,2,3]
[0,1,2] ["0","2"]

第一段里 3 是在迭代器创建后才 push 的,但遍历时照样读到了,这就是惰性的表现。需要一份固定的快照,就在创建迭代器之前先复制数组。 第二段是和 Object.keys 的区别。arr.keys() 按 length 从 0 数到末尾,空位的下标也算;Object.keys 只列出真实存在的属性,空位没有属性,所以被跳过了。另外 Object.keys 返回的是字符串数组,arr.keys() 产出的是数字。

最常用的写法:同时拿下标和值
for (const [i, item] of arr.entries()) 是最常见的用法。和 forEach((item, i) => ...) 相比,它可以用 break 提前退出,也可以在循环里 await。
回答前,多想一步

容易答错的地方

以为 entries() 返回的是二维数组
它返回的是迭代器,直接调用 arr.entries().map(...) 会报 TypeError。需要数组时先 Array.from(arr.entries()) 或者展开。
以为 arr.keys() 和 Object.keys(arr) 一样
差别有三个:前者是迭代器,后者是数组;前者产出数字,后者是字符串;前者包含空位下标,后者只包含实际存在的属性,还会包含你手动加在数组上的非数字属性。
试着用自己的话回答

面试官还会怎么问?

for...of 遍历数组时用的是哪个迭代器?

用的是 values(),因为 Array.prototype[Symbol.iterator] 和 Array.prototype.values 是同一个函数,所以 for (const x of arr) 和 for (const x of arr.values()) 效果一样。

Map 和 Set 也有这三个方法吗,有什么不同?

都有,返回的也是迭代器。Map 的 entries() 产出 [键, 值],是它的默认迭代器;Set 没有键的概念,keys() 和 values() 都产出元素本身,entries() 产出 [元素, 元素],这样设计是为了和 Map 的接口保持一致。

迭代器读完后能不能手动重置?

不能。数组迭代器没有提供重置的方法,读完后 next() 永远返回 { value: undefined, done: true }。需要反复使用就保存数组本身,每次用时重新调用 values(),或者一开始就用 Array.from 转成数组。

从一道题,走向一组知识

把知识连起来

数组与集合

怎么手写 Symbol.iterator,让普通对象支持 for...of 和展开?

自己手写一个迭代器,就能明白游标为什么走到末尾后不会自动重置。

数组与集合

稀疏数组的空位和 undefined 有什么区别,为什么 Array(n).map 不执行?

keys() 会列出空位下标而 Object.keys 不会,根源在于空位是「没有这个属性」。

参考资料

  • MDN:entries
  • MDN:Array

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

本题目录
  1. 先记住这个答案
  2. 返回值和「只能用一次」
  3. 迭代器是惰性的
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

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

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