前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
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 热点
旧版
首页程序员面试题库Array.at 负数索引优势
JavaScript数组与集合

arr.at(-1) 和 arr[-1] 有什么区别?越界和旧浏览器怎么办?

at(-1) 会把负数换算成 length - 1,拿到最后一项。arr[-1] 不做任何换算,它读的是一个名叫 "-1" 的普通属性,一般是 undefined。另外 at 越界也返回 undefined,旧环境可以用 arr[arr.length - 1] 兜底。

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

同样写 -1,意思完全不同

  1. arr.at(-1)方法调用,负数加上 length 再取,得到最后一项
  2. arr[-1]属性访问,-1 被转成字符串 "-1" 当属性名
  3. arr[arr.length - 1]老写法,任何环境都能用,但数组名要写两遍

at 不只数组有,字符串和 TypedArray 也有同名方法,"hello".at(-1) 就是 "o"。

核心回答

先记住这个答案

arr.at(-1) 是方法调用,它会把负数索引加上数组长度再去取值,所以得到最后一项。arr[-1] 是普通的属性访问,JavaScript 数组没有负数下标的概念,-1 会被转成字符串 "-1" 当作属性名,除非你专门设置过这个属性,否则就是 undefined。而 at 越界时返回 undefined,不报错;它只能读,不能写。at 是 ES2022 加入的,目标环境不支持时,用 arr[arr.length - 1] 或者引入 polyfill。

  • at(-n) 等于 arr[arr.length - n]
  • arr[-1] 读的是字符串属性 "-1",不是最后一项
  • 越界和空数组都返回 undefined,不报错
  • at 只能读,改最后一项用 arr.with(-1, v) 或下标赋值

一段代码看清 at 和方括号

先把几种写法放在一起跑一遍。

负数、越界、小数和字符串JavaScript
const arr = ['a', 'b', 'c']
console.log(arr.at(-1)) // 最后一项
console.log(arr[-1]) // 找的是名为 "-1" 的属性

arr[-1] = 'x' // 只是加了个普通属性
console.log(arr.length, arr.at(-1)) // 长度没变,最后一项也没变

console.log(arr.at(5), arr.at(-4)) // 两头越界
console.log(arr.at(1.7), 'hello'.at(-1)) // 小数向零截断;字符串也能用
查看输出与解释
c
undefined
3 c
undefined undefined
b o

arr[-1] = 'x' 这一行最能说明问题。它没有改最后一项,也没有改长度,只是在数组对象上挂了一个键为 "-1" 的属性,就像给普通对象加字段。数组的 length 只跟非负整数下标有关。 at 的换算规则是:先把参数转成整数(小数向零截断,不传或 NaN 当 0),负数就加上 length,结果不在 0 到 length - 1 之间就返回 undefined。所以 at(-4) 换算后是 -1,仍然越界。

旧环境怎么兜底

at 属于 ES2022,较老的浏览器和运行时没有这个方法,直接调用会报 TypeError: arr.at is not a function。有两种处理方式。

自己写一个等价的取值函数JavaScript
function at(list, index) {
  const i = Math.trunc(index) || 0 // 小数截断,NaN 和 -0 都当 0
  const k = i < 0 ? list.length + i : i
  return k >= 0 && k < list.length ? list[k] : undefined
}

console.log(at([1, 2, 3], -1), at([1, 2, 3], -4), at([], -1))
查看输出与解释
3 undefined undefined

只取最后一项的话,直接写 arr[arr.length - 1] 最简单,空数组时是 arr[-1],同样得到 undefined。需要支持任意负数时,用上面这种函数,或者在项目里通过 core-js 引入 polyfill,Babel 配合 useBuiltIns 可以按目标浏览器自动注入。

at 的真正好处
好处主要在链式调用里。比如 list.filter(isValid).at(-1),不用为了拿长度先把中间结果存成变量。以前的替代写法 slice(-1)[0] 也能做到,但会多创建一个数组,读起来也绕。
回答前,多想一步

容易答错的地方

以为 JavaScript 数组支持负数下标
Python 的 list[-1] 是最后一项,JavaScript 不是。方括号里的任何值都会先转成属性名,只有非负整数形式的属性名才算数组元素。
想用 at 修改元素
arr.at(-1) = 'x' 会报错,函数调用的结果不能作为赋值目标,Chrome 和 Node.js 里执行到这一行会抛 ReferenceError: Invalid left-hand side in assignment。要改最后一项,写 arr[arr.length - 1] = 'x';如果不想改原数组,ES2023 的 arr.with(-1, 'x') 会返回一个替换后的新数组,它也支持负数索引。
以为越界会抛错
at 越界、空数组调用都只是返回 undefined。所以拿到 undefined 时分不清是越界还是这一项本来就存的 undefined,需要区分时先判断长度。
试着用自己的话回答

面试官还会怎么问?

at 能用在类数组对象上吗?

可以借用。at 只依赖 length 和数字下标,所以 Array.prototype.at.call(arguments, -1) 能拿到最后一个参数。NodeList 本身没有 at,同样可以这样借用,或者先用 Array.from 转成数组。

at(-0) 返回什么?

返回第一项。-0 转成整数还是 0,不小于 0,所以按正数处理。这也说明「负数从末尾数」是按数值判断的,-0 不算负数。

字符串的 at 和 charAt 有什么不同?

charAt 不支持负数,"abc".charAt(-1) 返回空字符串;at(-1) 返回最后一个字符。越界时 charAt 返回空字符串,at 返回 undefined。两者都按 UTF-16 编码单元取,emoji 这类字符可能只取到半个。

从一道题,走向一组知识

把知识连起来

数组与集合

Array.fill 传负数的 start、end 怎么换算?为什么最后一项没被填上?

fill 的负数 start、end 也是加上 length 换算,规则和 at 一样,只是多了区间。

数组与集合

Array.from 能转换类数组,为什么数组展开有时却会报错?

at 要靠 length 和下标工作,Set 这类只能迭代的对象要先用 Array.from 转成数组。

参考资料

  • MDN:at

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

本题目录
  1. 先记住这个答案
  2. 一段代码看清 at 和方括号
  3. 旧环境怎么兜底
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

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

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