JavaScript比较运算符结果由抽象关系比较算法决定:先按类型分流,字符串直接字典序比较,非字符串则统一转数字;ToPrimitive调用顺序受LeftFirst控制,NaN参与比较时所有关系运算符均返回false。

要真正理解 JavaScript 中 <、>、<=、>= 这些比较运算符如何得出结果,关键不是死记“字符串转数字”或“对象调 valueOf”,而是抓住 Abstract Relational Comparison Algorithm(抽象关系比较算法) 的执行路径和两个决定性规则:类型优先级与转换顺序。
字符串比较走字典序,其他情况统一转数字
算法第一步就做分流:
- 如果 两边都是原始字符串(primitive string),直接按 Unicode 码点逐字符比,不转数字。例如
"2" < "10"是false,因为"2"首字符'2'(U+0032)码点大于'1'(U+0031); - 只要有一边不是字符串(比如数字、布尔、
null、undefined、对象等),就进入“非字符串分支”:双方先调ToPrimitive得到原始值,再统一用ToNumber转成数字比较。
ToPrimitive 决定谁先“露底”,LeftFirst 控制顺序
ToPrimitive 不是无脑转字符串或数字——它会按对象的 [Symbol.toPrimitive]、valueOf()、toString() 顺序尝试,但最终返回哪个原始值,取决于比较时的 LeftFirst 标志位:
-
a < b:LeftFirst = true→ 先对a调ToPrimitive,再对b调; -
a > b:本质是b < a,所以LeftFirst = false→ 先对b调ToPrimitive,再对a调; - 这个顺序会影响副作用触发时机(比如
valueOf里有 console.log),也影响某些边界对象的转换结果(如自定义[Symbol.toPrimitive]返回不同类型的值)。
数字转换失败时,结果为 undefined,整个比较返回 false
进入非字符串分支后,若任一操作数经 ToNumber 后为 NaN(比如 undefined、空对象 {}、无法解析的字符串 "abc"),则比较器返回 undefined,而算法规定此时 <、>、<=、>= 均返回 false:
-
{} > 0→ToPrimitive({})得"[object Object]"→ToNumber("...")得NaN→ 返回false; -
null > -1→ToNumber(null)是0,0 > -1为true; -
undefined < 1→ToNumber(undefined)是NaN→ 返回false(注意不是true)。
避开陷阱的实用建议
日常开发中不需要手写 ToPrimitive,但需警惕隐式转换带来的反直觉结果:
- 比较前主动转类型:
Number(a) < Number(b)或String(a) < String(b),消除歧义; - 避免用
>/<比较可能含字符串的变量(如表单输入),尤其当业务逻辑依赖数值大小时; - 调试时可用
console.log(typeof x, x + 0, Number(x))快速验证实际参与比较的数字值; - 对自定义对象,显式定义
[Symbol.toPrimitive](hint),并根据hint("number" 或 "string")返回预期原始值。

















