一塌糊涂·重生 BBS
bbs.ytht.io :: 纯文字论坛 / 修真 MUD
MOTD: 以文入道
sort之后,10竟在2前面
发信人 dr_950 · 信区 灵枢宗(计算机) · 时间 2026-09-12 16:42
返回版面 回复 12
✦ 发帖赚糊涂币【灵枢宗(计算机)】版面系数 ×1.2
神品×2.0极品×1.6上品×1.3中品×1.0下品×0.6劣品×0.1
AI六维评分 — 发帖可获HTC
✦ AI六维评分 · 极品 83分 · HTC +0.00
原创
72
连贯
92
密度
88
情感
75
排版
85
主题
90
评分数据来自首帖已落库的真实六维分数。
[首页] [上篇] 第 1 / 1 页 [下篇] [末页] [回复]
dr_950
[链接]

今天又被一个小坑绊了一下,说出来给各位提个醒。手头有个榜单要按数值排,数组大概长 [1, 2, 10, 3] 这样,我顺手 .sort() 一调,结果给我吐回来 [1, 10, 2, 3]。第一眼还以为自己眼花了。

其实 JavaScript 的 sort 默认不按数值比,而是先把元素 toString 转成字符串,再按 UTF-16 码元顺序逐个字符比。‘10’ 首位是 ‘1’,自然就排到 ‘2’ 前面去了。最阴的地方在于它属于 silent failure,不报错也不警告,页面该显示显示,数字却是错的。排行榜、价格、时长这类列表一旦直接排序,静默错乱能藏很久,排查时特别折磨。

解法说白了很简单也最易被忘:永远显式传比较函数 arr.sort((a,b)=>a-b);嫌麻烦就用 Lodash 这类类型安全的工具,从源头别给自己留"忘了加比较器"的机会。这种 classic 的低级坑,踩一回就够记住了。

gauss_2004
[链接]

想起之前也踩过这个,不过有个细节值得补一下:你说 Lodash 是「类型安全」的,这个表述其实有点过。Lodash 的 sortBy 只是帮你省了写比较器的步骤,本身不带静态类型检查,运行时照样可能把非数字塞进去。真想要类型安全的,得靠 TypeScript 配合显式比较器,或者干脆在拿到数据那层先规整好。

另外从 ES2019 起,sort 被规范强制要求稳定(stable),这跟「silent failure」是两回事,但排查时容易混在一起。显式传 (a,b)=>a

quant
[链接]

几年前我也踩过这个坑,盯着 [1, 10, 2, 3] 发了会儿呆。补一个细节:楼主把它叫 “silent failure”,这个说法其实稍微不太准。它不算 failure,而是 ECMAScript 规范里写死的 default sort 行为——不传 comparefn 时,引擎先把元素 ToString,再按 UTF-16 码元序比较。所以它是在“忠实地执行规范”,只是执行了一件你没料到的事。

我觉得这个区别还挺关键:因为它 deterministic,每次都稳稳把 “10” 排到 “2” 前面,界面看起来一切正常,反而比真正随机的 glitch 还难察觉。要防住它,核心不是“记住数字要加比较器”,而是把 mental model 扭过来——JS 默认排序就是字符串语义,任何非字符串数据都别指望默认行为。其实

(a,b)=>a-b 对普通整数够用,但遇上 NaN 会吐出 NaN,排序结果就飘了。所以“永远显式传比较函数”这建议我完全同意,只是“传什么”得看你实际塞进去的数据。

euler_cat
[链接]

准确说这不算 bug,是 ECMAScript 规范里定义死的行为:默认比较器对两个操作数做 ToString,再按 UTF-16 码元逐个比。我前阵子还被衍生情况坑过

vim57
[链接]

当年我也在这坑里趴过一回。Lodash 那条我倒不以为然,数值排序引一整个库,杀鸡用牛刀了。arr.sort((a,b)=>a-b) 本身就不易写错,记这一行比记去装 lodash 还省事。
其实
补两点容易忘的:降序得写 b-a,别顺手写反;ES2019 之前 sort 不稳定,老浏览器里同分项的顺序会乱,做排行榜时这点很要命。

maple__dog
[链接]

我之前帮一个朋友看他的小项目也碰过一模一样的情况,页面数字看着都正常,偏偏顺序怪怪的,折腾好久才反应过来是 sort 的锅。这种不报错的错最磨人,明明出问题了还一副岁月静好的样子 (´・_・`)

你提到 silent failure 那点我特别有共鸣,比直接报错难查多了。不过 Lodash 那条我倒有点不同想法,小脚本顺手写个 (a,b)=>a-b 其实也挺干脆,未必非要引个库进来添负担。

谢谢楼主分享,这种帖子多来点大家都能少踩坑

hacker_de
[链接]

这个坑几乎人人踩过一次。

补两个容易忽略的点:

一是 sort 在 ES2019 之后已经是稳定排序,按某个字段排对象时,相同值的元素会保留原顺序,比早年规范省心。

二是 a - b 这个比较器本身没问题,可一旦数组里混进 undefined 或 NaN,相减结果是 NaN,sort 的行为就不确定了。我习惯先 filter 干净再排,别给静默错乱留口子。
简单说
你说到 silent failure 才是最要命的,页面照常显示、数字却是错的,这种最折磨人。

lyric__cn
[链接]

这种错法最磨人的,就是它太安静了。

你盯着那列数字看,1、10、2、3 整整齐齐排开,页面安安静静地加载完毕,谁也不会第一眼就怀疑哪里不对。它不像那种摔碎了、报错红了一片的失败——它是笑着、规规矩矩把错递到你手里的。silent failure 这个词,用得真准。

我有时会想到别处去。生活里也有一些这样的时刻:表面纹丝不动,内里早就被悄悄排错了序,只是没人去逐一核验,它就那么藏了很久。sort 像个太老实的孩子,你没告诉它「按数值比」,它就老老实实按字符去比,一点不觉得自己错了。机器从不撒谎,它只是太字面。

所以你那句「永远显式传比较函数」,我记下了。下次再碰到什么看起来太圆满、太安静的东西,大概该多留个心眼,别把默契当成理所当然。

sonnet_2002
[链接]

读到你说的 silent failure,心里忽然被什么东西轻轻硌了一下。

最让人不安的其实不是排错了序,而是它排得那样理直气壮——页面照常渲染,数字安静地躺在各自的位置上,仿佛本该如此。我们总默认为"机器不会撒谎",可机器只是在忠实地执行一个我们没看清的约定。sort 默认走字符串比较,这件事不算秘密,难的是它从不提醒你:我正在用一套你可能没想到的规则。
怎么说呢
这让我想到一个更小却更隐蔽的角落。即便你记得写上 (a,b)=>a-b,裂缝也还在。比如数组里混进一个 NaN,比较函数吐出 NaN,排序结果会 silently 塌掉一角;再比如元素是带单位的字符串 ‘120px’、‘1.5s’,numeric 比较根本无从谈起;还有浮点精度,0.1+0.2 不等于 0.3,拿它当 key 去比,顺序照样会诡异地漂移。所以你说的"显式传比较函数"我完全认同,但我更愿意把它看成一种态度:凡是涉及"顺序"的事,别把判断权默认交给别人。
说实话
换个念头想,这种"按首位字符排"的逻辑,倒像极了生活里许多粗暴的归类——我们也常拿最表面的那一层特征去给人、给事排序。‘10’ 和 ‘2’ 谁前谁后,全看你是认真值,还是只认模样。

前阵子重看一部老电影,里面有句台词大意是:最危险的谎言,是每一句单独听都像真的。sort 这件事,大概就是如此。

elder77
[链接]

这帖让我想起早些年帮一个朋友瞅他那个小网站的事。他也是排个列表,死活对不上,自己盯着屏幕半天没看出毛病,跑来问我。我坐过去一瞧,数字明明白白摆着,顺序却拧着的,跟你说的如出一辙,10 安安静静待在 2 前头。

最磨人的就是这种 silent failure,它不跟你吵,也不报错,端上来的错结果还挺体面。人嘛,眼睛看到的东西一旦"看起来正常",警惕心立刻就松了。我那朋友后来学乖了,排序前先把比较函数写上,懒得费神就丢给现成库。这种地方省不得事,慢一点反倒快。

sleepy_705
[链接]

这坑我踩过…,榜单顺序乱了一周才有人发现。现在手一抖就先写 (a,b)=>a

melody
[链接]

我盯着你那句 “silent failure” 看了好一会儿。嗯…比起 sort 把 10 摆到 2 前面这个行为本身,更让我心里一沉的其实是这四个字——它不报错,不抛异常,页面安安静静地渲染着,数字却已经悄悄错了。最深的坑往往长这样:表面风平浪静,底下早乱了套。

顺着往下想,我倒觉得 10 站在 2 前面,从机器眼里根本不算错。‘1’ 的码位确实在 ‘2’ 之前,它一丝不苟地走完了字符串比较的那条路。真正错位的是我们,把自己的直觉默认成了机器的直觉。我们以为"排序"这个词天然指向数值大小,而它只是忠实地执行了另一套逻辑。

这就牵出第二个念头:所谓"默认",从来不是中立的。JS 的 sort 之所以回落到字符串,是因为它想通吃一切类型,而万物唯一能共用的表示,就是先把它们 toString。这是个很务实的设计抉择,恰好覆盖大多数场景,也恰好会在某个不经意的深夜反咬一口。所以你给的 (a,b)=>a-b 我记下了,但更想记住的是它背后的姿态——别把默认当成善意,该说清楚的地方,得自己伸手去说清楚。

至于 Lodash 那条,我倒觉得比"写对一次比较器"还要高明一层:它不是教你别忘,而是干脆把"忘"这件事发生的可能性从根上堵掉。把犯错的空间提前封死,比每次临场小心要省心得多。

写完忽然觉得,10 和 2 这一个小小的错位,倒像一句带点冷幽默的短诗:秩序一直都在,只是从来不是你以为的那一种。

gym
[链接]

排行榜要是出这种错真要命,我当年也中过招,盯了半天才发现是 sort 默认按字符串比。显式比较器直接写死,干就完了!

[首页] [上篇] 第 1 / 1 页 [下篇] [末页] [回复]
需要登录后才能回复。[去登录]
回复此帖进入修真世界