数组at()方法:负索引的救赎与JavaScript标准化之路
数组at()方法:负索引的救赎与JavaScript标准化之路
从一次代码评审说起
在某次团队代码评审中,小白注意到有同事写下了这样的代码:
const lastItem = arr[arr.length - 1];
这让我回想起自己早期开发时被负索引问题困扰的经历。今天,随着ES2022的发布,我们终于迎来了官方解决方案——Array.prototype.at()。本文将带你深入理解这一新特性背后的设计哲学与技术细节。
一、历史痛点:负索引的N种民间解法
1.1 传统方案的风险
(延续我的第二篇博客中提到的负索引问题)
// 方案一:手动计算索引
function getLast(arr) {return arr[arr.length - 1]; // 当arr为空时返回undefined
}// 方案二:使用slice
const last = arr.slice(-1)[0]; // 需要处理空数组情况
常见问题:
- 空数组处理需要额外判断(
arr.length > 0) - 链式操作时产生中间数组(性能损耗)
- 代码可读性差(特别是多层嵌套时)
1.2 社区解决方案对比
| 方案 | 优点 | 缺点 |
|---|---|---|
arr[length -1] | 无依赖 | 无法处理动态索引 |
slice(-n)[0] | 支持负索引 | 产生临时数组 |
Lodash 的 _.nth | 功能全面 | 增加依赖项 |
自定义getAt函数 | 可定制化 | 需要维护工具函数 |
二、at()方法:化繁为简的艺术
2.1 基础用法演示
const arr = ['a', 'b', 'c', 'd'];// 正索引
arr.at(0); // 'a' (等价于arr[0])
arr.at(2); // 'c'// 负索引
arr.at(-1); // 'd' (等效于arr[arr.length -1])
arr.at(-3); // 'b'// 越界访问
arr.at(10); // undefined
2.2 与传统写法的对比
场景:获取用户列表最后一位的注册时间
// 传统写法
const lastUser = users.length > 0 ? users[users.length -1] : null;
const regTime = lastUser?.registrationTime;// at()写法
const regTime = users.at(-1)?.registrationTime;
优势:减少中间变量,提升代码可读性
三、引擎揭秘:at()如何工作
3.1 V8引擎实现解析
通过Chrome DevTools调试V8源码,我们发现关键逻辑:
// v8/src/objects/js-array.cc
MaybeHandle<Object> JSArray::At(Handle<JSArray> array, int index) {// 索引转换逻辑int length = GetLength(*array);int actual_index = index < 0 ? length + index : index;// 边界检查if (actual_index < 0 || actual_index >= length) {return isolate->factory()->undefined_value();}// 直接访问Elements数组return array->GetElement(isolate, actual_index);
}
3.2 性能基准测试
使用jsbench.me测试100万次操作:
| 操作 | Chrome 102 | Firefox 100 |
|---|---|---|
arr[arr.length-1] | 12ms | 15ms |
arr.at(-1) | 13ms | 14ms |
arr.slice(-1)[0] | 245ms | 320ms |
结论:原生实现的at()几乎没有性能损耗
四、标准演进:一个提案的诞生
4.1 TC39提案阶段解析
(扩展我的第二篇博客中提到的标准化过程)
4.2 关键争议点
-
方法命名:
- 候选名称包括
item(),nth(),get() - 最终选择
at()保持与其他语言(如Python)的一致性
- 候选名称包括
-
越界行为:
- 保持与
[]操作符一致(返回undefined) - 否决抛出错误的提议(避免破坏性变更)
- 保持与
五、实战应用场景
5.1 链式操作优化
// 获取二维数组最后一个元素的属性
const matrix = [[{ id: 1 }, { id: 2 }], [{ id: 3 }]];
const lastId = matrix.at(-1)?.at(-1)?.id; // 3// 传统写法对比
const lastRow = matrix[matrix.length -1];
const lastId = lastRow ? lastRow[lastRow.length -1]?.id : undefined;
5.2 可迭代对象通用方案
// 适用于所有实现了可索引接口的对象
function logLast(...collections) {collections.forEach(col => {console.log(col.at?.(-1));});
}logLast([1,2,3], new Uint8Array([4,5,6]), 'abc');
// 输出: 3, 6, 'c'
六、浏览器兼容与优雅降级
6.1 特性检测方案
// 安全检测函数
function supportAtMethod() {try {return typeof [].at === 'function' && [1].at(-1) === 1;} catch {return false;}
}// 使用示例
if (supportAtMethod()) {// 使用原生实现
} else {// 加载polyfill
}
6.2 推荐polyfill
// 官方推荐实现(已考虑稀疏数组等边界情况)
if (!Array.prototype.at) {Array.prototype.at = function(n) {const len = this.length;n = Math.trunc(n) || 0;if (n < 0) n += len;return (n < 0 || n >= len) ? undefined : this[n];};
}
七、思考题:为什么at()不修改原数组?
从JavaScript语言设计哲学的角度分析,以下哪些原因最可能成立?
- A. 保持与
[]操作符行为一致 - B. 避免破坏函数式编程范式
- C. 减少引擎实现复杂度
- D. 预留方法重载的可能性
欢迎在评论区分享你的见解!
写在最后
at()方法的价值不仅在于简化代码,更体现了JavaScript语言在保持向后兼容的同时持续改进开发者体验的决心。正如TC39委员会成员所说:“好的语言特性应该是让开发者发现后惊呼’这本来就应该存在!'”。现在,是时候让你的代码告别arr[arr.length -1]了。
相关文章:
数组at()方法:负索引的救赎与JavaScript标准化之路
数组at()方法:负索引的救赎与JavaScript标准化之路 从一次代码评审说起 在某次团队代码评审中,小白注意到有同事写下了这样的代码: const lastItem arr[arr.length - 1];这让我回想起自己早期开发时被负索引问题困扰的经历。今天…...
HTML<label>标签
例子 三个带标签的单选按钮: <form action"/action_page.php"> <input type"radio" id"html" name"fav_language" value"HTML"> <label for"html">HTML</label><br&…...
约瑟夫问题(信息学奥赛一本通-2037)
【题目描述】 N个人围成一圈,从第一个人开始报数,数到M的人出圈;再由下一个人开始报数,数到M 的人出圈;…输出依次出圈的人的编号。 【输入】 输入N和M。 【输出】 输出一行,依次出圈的人的编号。 【输入样…...
二分查找题目:寻找两个正序数组的中位数
文章目录 题目标题和出处难度题目描述要求示例数据范围 解法一思路和算法代码复杂度分析 解法二思路和算法代码复杂度分析 题目 标题和出处 标题:寻找两个正序数组的中位数 出处:4. 寻找两个正序数组的中位数 难度 8 级 题目描述 要求 给定两个大…...
【技术洞察】2024科技绘卷:浪潮、突破、未来
涌动与突破 2024年,科技的浪潮汹涌澎湃,人工智能、量子计算、脑机接口等前沿技术如同璀璨星辰,方便了大家的日常生活,也照亮了人类未来的道路。这一年,科技的突破与创新不断刷新着人们对未来的想象。那么回顾2024年的科…...
【Linux】gdb——Linux调试器
gdb使用背景 程序的发布方式有两种,debug模式和release模式 Linux gcc/g出来的二进制程序,默认是release模式 要使用gdb调试,必须在源代码生成二进制程序的时候, 加上 -g 选项 gdb使用方法 首先进入gdb gdb test_glist显示代码 断点 b 行…...
fpga系列 HDL:XILINX Vivado Vitis 高层次综合(HLS) 实现 EBAZ板LED控制(上)
目录 创建工程创建源文件并编写C代码C仿真综合仿真导出RTL CG导出RTL错误处理: 创建工程 创建源文件并编写C代码 创建源文件(Souces下的hlsv.h和hlsv.cpp,Test Bench下的test_hlsv1.cpp): hlsv1.h #ifndef HLSV1 #define HLSV1 #include &l…...
卡特兰数学习
1,概念 卡特兰数(英语:Catalan number),又称卡塔兰数,明安图数。是组合数学中一种常出现于各种计数问题中的数列。它在不同的计数问题中频繁出现。 2,公式 卡特兰数的递推公式为:f(…...
度小满Java开发面试题及参考答案 (上)
String 是基本类型吗?String、StringBuffer、StringBuilder 的区别是什么?拼接字符串有哪些做法? String 不是基本类型,它是 Java 中的一个类,属于引用类型。 下面来看看 String、StringBuffer、StringBuilder 的区别: 类型可变性线程安全性性能适用场景String不可变线程…...
Python-基于PyQt5,json和playsound的通用闹钟
前言:刚刚结束2024年秋季学期的学习,接下来我们继续来学习PyQt5。由于之前我们已经学习了PyQt5以及PyUIC,Pyrcc和QtDesigner的安装,配置。所以接下来我们一起深入PyQt5,学习如何利用PyQt5进行实际开发-基于PyQt5,json和…...
关于数字地DGND和模拟地AGND隔离
文章目录 前言一、1、为什么要进行数字地和模拟地隔离二、隔离元件1.①0Ω电阻:2.②磁珠:3.电容:4.④电感: 三、隔离方法①单点接地②数字地与模拟地分开布线,最后再PCB板上一点接到电源。③电源隔离④、其他隔离方法 …...
小识Java死锁是否会造成CPU100%?
死锁或者大量的死锁不一定会直接导致CPU占用率达到100%。以下是详细分析: 一、死锁对CPU的影响 资源占用:死锁是指两个或多个线程(或进程)在相互等待对方释放资源,导致所有涉及的线程都无法继续执行。在死锁状态下&a…...
DeepSeek R1学习
0.回顾: https://blog.csdn.net/Together_CZ/article/details/144431432?ops_request_misc%257B%2522request%255Fid%2522%253A%25226574a586f0850d0329fbb720e5b8d5a9%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id…...
激光线扫相机无2D图像的标定方案
方案一:基于运动控制平台的标定 适用场景:若激光线扫相机安装在可控运动平台(如机械臂、平移台、旋转台)上,且平台的运动精度已知(例如通过编码器或高精度步进电机控制)。 步骤: 标…...
12 款开源OCR发 PDF 识别框架
2024 年 12 款开源文档解析框架的选型对比评测:PDF解析、OCR识别功能解读、应用场景分析及优缺点比较 这是该系列的第二篇文章,聚焦于智能文档处理(特别是 PDF 解析)。无论是在模型预训练的数据收集阶段,还是基于 RAG…...
【反悔堆】【hard】力扣871. 最低加油次数
汽车从起点出发驶向目的地,该目的地位于出发位置东面 target 英里处。 沿途有加油站,用数组 stations 表示。其中 stations[i] [positioni, fueli] 表示第 i 个加油站位于出发位置东面 positioni 英里处,并且有 fueli 升汽油。 假设汽车油…...
为什么应用程序是特定于操作系统的?[计算机原理]
你把WINDOWS程序复制到MAC上使用,会发现无法运行。你可能会说,MAC是arm处理器,而WINDWOS是X86 处理器。但是在2019年,那时候MAC电脑还全是Intel处理器,在同样的X86芯片上,运行MAC和WINDOWS 程序还是无法互相…...
多项日常使用测试,带你了解如何选择AI工具 Deepseek VS ChatGpt VS Claude
多项日常使用测试,带你了解如何选择AI工具 Deepseek VS ChatGpt VS Claude 注:因为考虑到绝大部分人的使用,我这里所用的模型均为免费模型。官方可访问的。ChatGPT这里用的是4o Ai对话,编程一直以来都是人们所讨论的话题。Ai的出现…...
什么是循环神经网络?
一、概念 循环神经网络(Recurrent Neural Network, RNN)是一类用于处理序列数据的神经网络。与传统的前馈神经网络不同,RNN具有循环连接,可以利用序列数据的时间依赖性。正因如此,RNN在自然语言处理、时间序列预测、语…...
Flink运行时架构
一、系统架构 1)作业管理器(JobManager) JobManager是一个Flink集群中任务管理和调度的核心,是控制应用执行的主进程。也就是说,每个应用都应该被唯一的JobManager所控制执行。 JobManger又包含3个不同的组件。 &am…...
网络工程师 (6)操作系统概述
一、操作系统的定义 (一)基本定义 操作系统(Operating System,简称OS)是计算机系统中至关重要的基础性系统软件。它是计算机硬件与上层软件之间的桥梁,负责管理和控制整个计算机系统的硬件和软件资源&…...
【2025年数学建模美赛C题】第1-5问F奖解题思路+高级绘图+可运行代码
基于多模型分析的奥运会奖牌预测与影响因素研究 解题思路一、问题重述二、问题分析三、模型假设与符号说明四、数据预处理五、奖牌榜预测5.1 基于LSTM长短期记忆循环神经网络的预测模型的建立5.2 模型预测结果 六、首枚奖牌预测6.1 BP神经网络的建立6.2 模型预测结果 七、各国奖…...
StarRocks 安装部署
StarRocks 安装部署 StarRocks端口: 官方《配置检查》有服务端口详细描述: https://docs.starrocks.io/zh/docs/deployment/environment_configurations/ StarRocks架构:https://docs.starrocks.io/zh/docs/introduction/Architecture/ Sta…...
RoboMaster- RDK X5能量机关实现案例(一)识别
作者:SkyXZ CSDN:https://blog.csdn.net/xiongqi123123 博客园:https://www.cnblogs.com/SkyXZ 在RoboMaster的25赛季,我主要负责了能量机关的视觉方案开发,目前整体算法已经搭建完成,实际方案上我使用的上…...
llama.cpp LLM_ARCH_DEEPSEEK and LLM_ARCH_DEEPSEEK2
llama.cpp LLM_ARCH_DEEPSEEK and LLM_ARCH_DEEPSEEK2 1. LLM_ARCH_DEEPSEEK and LLM_ARCH_DEEPSEEK22. LLM_ARCH_DEEPSEEK and LLM_ARCH_DEEPSEEK23. struct ggml_cgraph * build_deepseek() and struct ggml_cgraph * build_deepseek2()References 不宜吹捧中国大语言模型的同…...
检测到联想鼠标自动调出运行窗口,鼠标自己作为键盘操作
联想鼠标会自动时不时的调用“运行”窗口 然后鼠标自己作为键盘输入 然后打开这个网页 (不是点击了什么鼠标外加按键,这个鼠标除了左右和中间滚轮,没有其他按键了)...
-bash: ./uninstall.command: /bin/sh^M: 坏的解释器: 没有那个文件或目录
终端报错: -bash: ./uninstall.command: /bin/sh^M: 坏的解释器: 没有那个文件或目录原因:由于文件行尾符不匹配导致的。当脚本文件在Windows环境中创建或编辑后,行尾符为CRLF(即回车和换行,\r\n)…...
15天基础内容总复习
总复习 一.day01内容 1.JVM,JRE,JDK的关系 JVM: java虚拟机,用来运行java程序的,JVM本身是不夸平台的,每个操作系统都需要安装针对本操作系统的JVM所以: java通过jvm的不夸平台实现了java的跨平台JRE:java运行环境,包含jvm和核心类库JDK:java开发工具包,包含开发工具和JRE三…...
星火大模型接入及文本生成HTTP流式、非流式接口(JAVA)
文章目录 一、接入星火大模型二、基于JAVA实现HTTP非流式接口1.配置2.接口实现(1)分析接口请求(2)代码实现 3.功能测试(1)测试对话功能(2)测试记住上下文功能 三、基于JAVA实现HTTP流…...
如何将电脑桌面默认的C盘设置到D盘?详细操作步骤!
将电脑桌面默认的C盘设置到D盘的详细操作步骤! 本博文介绍如何将电脑桌面(默认为C盘)设置在D盘下。 首先,在D盘建立文件夹Desktop,完整的路径为D:\Desktop。winR,输入Regedit命令。(或者单击【…...
