前端进阶之旅前端进阶之旅
  • 基础篇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 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
首页程序员面试题库sort 会修改原数组吗
JSJavaScript数组与集合

Array.prototype.sort 会修改原数组吗?如果不想修改原数组该怎么做?

会。Array.prototype.sort() 直接在原数组上重排元素,并返回同一个数组的引用;若不想改变原数组,应先用 slice() 或展开运算符建立浅拷贝,再对新数组排序。

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

sort 的原地语义

  1. 原地排序直接调整原数组元素位置,不产生新数组
  2. 返回原引用返回值与原数组是同一对象,改返回值等于改原数组
  3. 浅拷贝保护slice 或展开产生的副本才能承载排序变化

toSorted 也返回浅拷贝,其元素对象引用仍与原数组共享。

核心回答

先记住这个答案

会。Array.prototype.sort() 是原地排序,排序过程中会直接重排原数组的元素,结束后返回的也还是那个数组对象的引用,因此原数组内容已被修改。若希望保留原数组,常见做法是先创建浅拷贝:用 original.slice().sort() 或 [...original].sort(),也可以使用 ES2023 的 original.toSorted()。要注意拷贝只是浅层,当数组元素是对象时,排序后的数组与原数组共享这些对象;若之后通过任一数组修改对象,原数组中对应的对象也会看到改变。

  • sort() 原地重排并修改原数组
  • 返回值是原数组的同一引用
  • 先浅拷贝再排序可保护原数组

sort 修改的是调用它的数组本身

Array.prototype.sort 会把排序结果写回调用它的数组,并返回这个数组本身。因此把返回值赋给另一个变量,并不会得到独立副本;两个变量指向同一数组,后续改动会互相可见。这是 API 的可观察行为,不要求引擎内部完全不分配辅助存储。

默认不传比较器时,元素会先被转成字符串,再按 UTF-16 码元升序排列,例如 [10, 9].sort() 的结果是 [10, 9],因为字符串 "10" 排在 "9" 前面。若希望按数值升序,必须传入 (a, b) => a - b。比较器只决定排序结果,不改变“原地重排”这一核心语义。

展示排序结果但保留原始顺序

一个工单后台按提交时间维护数组 tickets。用户要求点“按优先级排序”时只改变列表显示,原时间顺序不能被覆盖。直接调用 tickets.sort((a, b) => b.priority - a.priority) 会让 tickets 永久失去原顺序;应改为 const view = [...tickets].sort((a, b) => b.priority - a.priority) 并渲染 view。这样原数组仍然是提交时间顺序。

复制外层数组后,展示层可以读取 view,默认顺序继续保存在 tickets 中。恢复默认排序时切回 tickets 即可。这个方案适用于只调整排列顺序的视图:比较器读取优先级但不修改对象;若比较器自己修改对象、访问有副作用的 getter,则需要单独审查这些副作用。

新数组与共享元素要分开判断JavaScript
const tickets = [{ id: 'A', priority: 1 }, { id: 'B', priority: 3 }];
const view = [...tickets].sort((a, b) => b.priority - a.priority);
console.log(tickets.map(item => item.id).join(','));
console.log(view.map(item => item.id).join(','));
console.log(view === tickets);
console.log(view[0] === tickets[1]);
view[0].priority = 9;
console.log(tickets[1].priority);
查看输出与解释
A,B
B,A
false
true
9

普通 JavaScript 环境可运行。外层数组不同,所以顺序互不覆盖;内部工单对象仍被共享,因此通过 view 修改优先级也会改变 tickets 读取到的对象。复制数组与禁止修改对象是两个不同要求。

浅拷贝无法隔离所有变更

原数组是稀疏数组时,复制方式会带来不同结果。例如 const arr = ['a', 'c', , 'b']; 直接 arr.sort() 会把空位移到末尾,结果仍是空位;arr.slice().sort() 同样保留空位;但 [...arr].sort() 会先把空位读成 undefined,排序结果末尾出现显式 undefined。这种差异在普通连续数组上不可见,遇到来自接口或删除操作留下的稀疏数据时必须留意。

浅拷贝只复制数组最外层槽位,不复制元素对象。const original = [{ value: 2 }, { value: 1 }]; const clone = [...original].sort((a, b) => a.value - b.value); 之后,两个数组共享同样的两个对象,只是索引顺序不同。若排序完成后通过 clone[0].value = 99,original 中对应索引的对象也会被改动。要真正隔离对象级变化,需要额外做深拷贝,代价需按场景单独评估。

回答前,多想一步

容易答错的地方

sort 返回新数组
事实是 sort() 返回排序后的原数组引用,不是新建数组。可以运行 const a = [3, 1]; const b = a.sort(); console.log(b === a);,输出 true。若把它当成 map 那样的新数组使用,后续修改返回值会意外改动原数组。
浅拷贝一层就彻底不变
[...arr] 只隔离外层数组顺序。如果元素是对象,通过任一数组修改该对象,另一数组也能读到变化。深拷贝能在所支持的数据范围内隔离对象,但不自动建立只读约束;需要防止修改时,应通过数据所有权、不可变更新或合适的冻结策略约束写入。
试着用自己的话回答

面试官还会怎么问?

`toSorted` 和先复制再 `sort` 一定等价吗?

在已支持 toSorted 的环境里,对普通连续数组两者通常等价:都返回浅拷贝后的排序结果,原数组不受影响;差别在于 toSorted 直接返回新数组,调用方少写一步。但对稀疏数组二者并不等价:slice().sort() 会保留空位,而 toSorted 按索引读取时会把空位当作 undefined 取出并产出稠密数组,行为与 [...arr].sort() 一致,排序结果末尾是显式 undefined 而非空位。旧环境没有该方法时,用 slice().sort() 或 [...arr].sort() 保证兼容。

`slice` 和 `Array.from` 复制稀疏数组有何不同?

slice 会保留稀疏数组中的空位,Array.from 和展开运算符走迭代器协议,会把空位当作 undefined 取出。若要排序后仍区分 empty 与 undefined,应当优先使用 slice 复制。

在排序前先复制数组会影响性能吗?

会多一次 O(n) 的浅拷贝和额外内存,排序本身仍是主导开销。若需要多次读取同样的排序结果,不要每次重新排序,可缓存拷贝后的数组;若只是临时展示,复制一次的成本通常可以接受。

从一道题,走向一组知识

把知识连起来

相等比较

Object.is、=== 与 SameValueZero 的差别

比较排序前后的数组时,先区分引用身份与数组内容;Object.is 只回答前者。

函数与作用域

JavaScript 中 arguments 对象与函数参数(parameters)有何区别?

排序比较器接收两个元素参数,继续区分参数绑定与传入对象本身的可变性。

放回知识体系中复习JavaScript 进阶知识

参考资料

  • Array.prototype.sort()

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

本题目录
  1. 先记住这个答案
  2. sort 修改的是调用它的数组本身
  3. 展示排序结果但保留原始顺序
  4. 浅拷贝无法隔离所有变更
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

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

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