jstips 第 00 期:向数组中插入元素——尾部、头部、中间三种场景的最优写法与性能实测 教程【免费下载链接】jstipsThis is about useful JS tips!项目地址https://gitcode.com/gh_mirrors/js/jstips点击查看免费下载在 JavaScript 日常开发中向已有数组中插入元素是出现频率极高的操作向尾部追加用push向头部插入用unshift向中间写入用splice。但这三个众所周知的方法并非在每种场景下都是最高效的选择——通过下标直接赋值、借助concat生成新数组往往能获得显著更优的性能。本文以 jstips 项目一个收录短小实用 JavaScript 技巧的开源仓库的第 00 期 tip Insert item inside an Array 为主体完整复现其在移动端与桌面端多个浏览器上的基准测试数据并结合数组方法的语义差异原地修改 vs. 返回新数组帮助你在真实项目中选出兼顾性能与副作用行为的插入方案。一张表看懂插入位置与候选 API原文档开篇即给出结论向数组尾部插入用push头部用unshift中间用splice。但进一步对比后每种场景还有替代写法插入位置原地修改原数组返回新数组尾部arr.push(item)/arr[arr.length] itemarr.concat([item])头部arr.unshift(item)[item].concat(arr)中间arr.splice(index, 0, item)无splice即原地操作其中arr[arr.length] item与[item].concat(arr)正是原文档重点推荐的更高效的方式。向数组尾部添加元素push、下标赋值与concat之争var arr [1,2,3,4,5]; var arr2 []; arr.push(6); arr[arr.length] 6; arr2 arr.concat([6]);三种写法结果相同数组都变成[1,2,3,4,5,6]但语义有本质区别arr.push(6)原地修改原数组返回新长度arr[arr.length] 6由于length恒等于当前最大下标 1arr[arr.length]恰好指向末尾的下一个空位效果等同于push同样原地修改原数组arr.concat([6])不修改原数组而是返回一个包含原数组全部元素与新增元素的新数组因此必须用arr2接住返回值。原文档特别提醒前两种方法都会修改原始数组。如果调用方依赖原数组不被改动concat才是唯一安全的写法——这也是性能优先之外必须考虑的功能约束。移动端实测ops/sec越大越快原文档在 2015-2016 年代的移动端浏览器上测得以下数据浏览器arr.push(6)arr[arr.length] 6arr2 arr.concat([6])Android (v4.2.2)3 319 6943 319 694与前等同比其他两法慢 50.61%Chrome Mobile (v33.0.0)慢 66.74%6 125 975基准慢 87.63%Safari Mobile (v9)慢 40.19%7 452 898基准慢 49.78%移动端平均结论arr[arr.length] 6平均 5 632 856 ops/secarr.push(6)慢 35.64%arr.concat([6])慢 62.67%。桌面端实测浏览器arr.push(6)arr[arr.length] 6arr2 arr.concat([6])Chrome (v48.0.2564)慢 61.94%21 602 722基准慢 87.45%Firefox (v44)56 032 805基准慢 0.52%慢 87.36%IE (v11)慢 39.61%67 197 046基准慢 93.41%Opera (v35.0.2066.68)慢 71.60%30 775 071基准慢 83.70%Safari (v9.0.3)42 670 978基准慢 0.80%慢 76.07%桌面端平均结论arr[arr.length] 6平均 42 345 449 ops/secarr.push(6)慢 34.66%arr.concat([6])慢 85.79%。从这份数据可以读出两个稳定规律在绝大多数被测浏览器中arr[arr.length] item与push性能相当甚至更快而concat由于需要分配新数组并复制全部元素几乎总是最慢的慢 49%93%。差异最大的场景出现在 Firefox 与 Safari 上push与下标赋值几乎打平差距不足 1%。原文档当时通过 jsPerf 基准测试工具测得上述结果并提醒读者不同引擎的优化策略不同结论会随环境变化建议在自己的目标环境里复测。向数组头部添加元素unshift与[0].concat(arr)的对决var arr [1,2,3,4,5]; arr.unshift(0); [0].concat(arr);原文档特别点出两者差异unshift会编辑原始数组原地修改返回新长度而[0].concat(arr)返回一个新数组原数组保持不变。若要保留原数组用于后续逻辑必须使用后者并接收返回值。移动端实测浏览器arr.unshift(0)[0].concat(arr)Android (v4.2.2)慢 97.85%1 808 717基准Chrome Mobile (v33.0.0)慢 99.86%1 269 498基准Safari Mobile (v9)3 250 184基准慢 33.67%移动端平均结论[0].concat(arr)平均 4 972 622 ops/secarr.unshift(0)慢 64.70%。桌面端实测浏览器arr.unshift(0)[0].concat(arr)Chrome (v48.0.2564)慢 96.77%2 656 685基准Firefox (v44)慢 99.72%8 039 759基准IE (v11)慢 98.31%3 604 226基准Opera (v35.0.2066.68)慢 97.44%4 102 128基准Safari (v9.0.3)12 356 477基准慢 15.17%桌面端平均结论[0].concat(arr)平均 6 032 573 ops/secarr.unshift(0)慢 78.65%。头部插入的结果比尾部更悬殊除了 Safari 外[0].concat(arr)几乎全面碾压unshift差距普遍高达 96%99%。其原理并不难理解——unshift需要在数组头部腾出空位必须把原有元素全部后移一位相当于一次整体拷贝加移位而concat在部分引擎中可以利用内存复制等底层优化直接拼接出新数组避免了逐元素搬移的开销。当插入频率高、数组规模大时头部插入优先选择[item].concat(arr)并接受返回新数组的副作用是在这些被测引擎上的普遍更优策略。向数组中间添加元素splice依然是最优解var items [one, two, three, four]; items.splice(items.length / 2, 0, hello);Array.prototype.splice(start, deleteCount, ...items)的三个关键参数决定了它的行为start开始修改的位置索引。负数表示从数组末尾倒数大于数组长度时按数组长度处理deleteCount从start起要删除的元素个数。传0表示只插入、不删除——这正是向中间插入元素的标准用法...items要插入的元素可以是多个以逗号分隔依次插入。上例中items.length / 2 2因此元素hello被插入到索引 2 处two与three之间结果为[one, two, hello, three, four]。splice是原地修改方法会直接改写原数组。原文档明确给出结论在中间插入场景下splice是最简单也是最高效的方式作者在多款浏览器与操作系统上重复测试结果均保持一致。相比用拆成前后两段再重组的写法splice由引擎原生实现避免了额外的数组分配与拼接开销。实测数据的正确打开方式需要特别说明的是上文所有 ops/sec 数据均来自原文档在 2015-2016 年特定浏览器版本Android v4.2.2、Chrome Mobile v33、Safari Mobile v9、Chrome v48、Firefox v44、IE v11、Opera v35、Safari v9.0.3下通过 jsPerf 得到的测试结果。它们用于说明不同写法之间存在数量级差异这一结论非常直观但不代表当今引擎的绝对性能——V8、SpiderMonkey 等引擎对push、unshift的优化策略早已多次迭代。正如原文档结尾所建议的这些 tip 的价值在于启发你关注写法的性能差异并鼓励你在自己的目标浏览器与操作系统上自行测试用数据而不是直觉做决策。仓库中另一篇 tip Tip to measure performance of a JavaScript block 就专门介绍了用高精度时间戳测量 JS 代码块性能的方法可作为自建基准的起点。从 jstips 仓库延伸数组操作的完整工具箱本 tip 是 jstips 仓库README.md中编号为00的第一篇技巧。围绕数组增删改查这一主题仓库里还收录了多篇可以串联阅读的姊妹篇Two ways to empty an arraylist []只是把变量指向新数组、旧引用仍持有数据list.length 0才会真正清空原数组内容并同步影响所有引用——与本篇原地修改 vs. 新数组的语义一脉相承Shuffle an Array基于 Fisher-Yates 算法的原地洗牌用到了本文arr[i] arr[j]式的下标直写3 Array Hacks稀疏数组的map陷阱、用展开运算符传空参、[...new Set(arr)]去重Looping over arrayswhile、经典for、forEach、map、reduce、filter等遍历方案的完整梳理Array average and median基于reduce与排序求均值、中位数。这些 tip 共同勾勒出数组操作的最佳实践图谱。如果你希望为仓库贡献新技巧可以参考 CONTRIBUTING.md 的提交规范与 POST_TEMPLATE.md 的写作模板本 tip 还提供了 西班牙语版 与 简体中文版 翻译方便对照阅读。小结三种场景的最优选择插入位置首选写法备选写法注意事项尾部arr[arr.length] itemarr.push(item)两者均原地修改arr.concat([item])返回新数组但最慢头部[item].concat(arr)arr.unshift(item)在多数被测引擎中concat明显更快但返回新数组中间arr.splice(index, 0, item)—deleteCount 0表示只插入原地修改原数组最终的选择公式其实很简单优先用下标赋值或push写尾部、用concat拼接写头部、用splice写中间当副作用是否修改原数组比性能更重要时则一律改用concat系列写法。至于极端场景下的最优解记得回到自己的目标环境里跑一遍基准让数据说话。赞分享教程【免费下载链接】jstipsThis is about useful JS tips!项目地址https://gitcode.com/gh_mirrors/js/jstips点击查看免费下载相关推荐在 JavaScript 中获取数组头部head与尾部tail元素30-seconds-of-code 实战指南在 JavaScript 中获取数组头部head与尾部tail元素30 seconds of code 实战指南 数组的 头部head 指其第一个教程文档30 seconds of code 实战按条件从 JavaScript 数组头部或尾部提取元素30 seconds of code 实战按条件从 JavaScript 数组头部或尾部提取元素 本文基于 30 seconds of code 仓库中关于教程文档Webnovel Writer mainline_readyfalse如何快速用 preflight 和 doctor 四步定位Webnovel Writer mainline_readyfalse如何快速用 preflight 和 doctor 四步定位 Webnovel Writ人工智能AI 应用AI 写作RAGAI 插件AI 技能创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考