Tavio's blog Tavio's blog
首页
  • JVM底层原理
  • 邪恶多线程
  • MyBatis底层原理
  • Spring底层原理
  • MySQL的优化之路
  • ClickHouse的高性能
  • Redis的快速查询
  • RabbitMQ的生产
  • Kafka的高吞吐量
  • ES的入门到入坑
  • MySQL自增ID主键空洞
  • 前端实现长整型排序
  • MySQL无感换表
  • Redis延时双删
  • 高并发秒杀优惠卷
  • AOP无侵入式告警
  • 长短链接跳转
  • 订单超时取消
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Tavio Zhang

努力学习的小码喽
首页
  • JVM底层原理
  • 邪恶多线程
  • MyBatis底层原理
  • Spring底层原理
  • MySQL的优化之路
  • ClickHouse的高性能
  • Redis的快速查询
  • RabbitMQ的生产
  • Kafka的高吞吐量
  • ES的入门到入坑
  • MySQL自增ID主键空洞
  • 前端实现长整型排序
  • MySQL无感换表
  • Redis延时双删
  • 高并发秒杀优惠卷
  • AOP无侵入式告警
  • 长短链接跳转
  • 订单超时取消
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • MySQL自增ID主键空洞
  • 前端实现长整型排序
    • 一、先看现象:长整型排序的“诡异”行为
    • 二、根源剖析:JS的Number类型与长整型的“兼容性问题”
    • 三、解决方案:3种实用方案,覆盖不同场景
      • 后端返回时转为字符串,前端直接按字符串规则排序
      • 使用BigInt类型进行比较(ES2020+支持)
    • 四、方案对比与选择建议
    • 五、总结与注意事项
  • MySQL无感换表
  • Redis延时双删
  • 高并发秒杀优惠卷
  • AOP无侵入式告警
  • 长短链接跳转
  • 双 Token 登录
  • 订单超时取消
  • 实践
Tavio
2022-05-15
目录

前端实现长整型排序

在前端开发中,排序是高频需求之一。但当排序对象是长整型数据(通常指大于 2^53 - 1 的整数)时,往往会出现排序错乱的问题。这并非我们的排序逻辑错误,而是JavaScript本身对长整型的处理机制导致的。

# 一、先看现象:长整型排序的“诡异”行为

我们先看一个简单的测试案例,用默认的 sort() 方法对一组长整型数组排序:

// 包含长整型的数组(2^53 - 1 = 9007199254740991,以下数值均大于该值)
const longIntArr = [
  9007199254740993,
  9007199254740992,
  9007199254740995,
  9007199254740994
];

// 尝试排序
const sortedArr = longIntArr.sort((a, b) => a - b);
console.log(sortedArr); 
// 输出结果:[9007199254740993, 9007199254740992, 9007199254740995, 9007199254740994]
// 预期结果:[9007199254740992, 9007199254740993, 9007199254740994, 9007199254740995]
1
2
3
4
5
6
7
8
9
10
11
12
13

可以看到,排序结果完全不符合预期。即使我们传入了自定义的比较函数 (a, b) => a - b,也无法得到正确的排序结果。这背后的原因是什么?

# 二、根源剖析:JS的Number类型与长整型的“兼容性问题”

要理解这个问题,首先要明确JavaScript的数值存储机制:

  1. JS中没有专门的“整数类型”:所有数值(包括整数、小数)都用 Number 类型表示,而 Number 本质是双精度浮点数。
  2. 双精度浮点数的精度限制:IEEE 754标准规定,双精度浮点数的有效位数是53位(包括整数部分和小数部分)。这意味着,当整数的绝对值大于 2^53 - 1(即9007199254740991)时,无法被精确表示——多个不同的整数会映射到同一个浮点数。

回到排序场景:当我们对两个大于 2^53 - 1 的长整型进行 a - b 运算时,由于这两个数无法被精确表示,运算结果可能是0、正数或负数,导致比较函数返回错误的结果,最终排序错乱。

补充验证:我们可以用 Number.MAX_SAFE_INTEGER 获取安全整数的最大值(即 2^53 - 1),用 Number.isSafeInteger() 检测一个整数是否能被安全表示。

# 三、解决方案:3种实用方案,覆盖不同场景

针对长整型排序问题,核心思路是避免直接对长整型进行数值运算,转而通过其他方式比较大小。以下是2种常用方案,适用于不同的业务场景。

# 后端返回时转为字符串,前端直接按字符串规则排序

核心逻辑: 后端将长整型字段转为字符串格式返回。前端直接对字符串数组进行排序,避免长整型的精度丢失问题。

代码实现:

// 后端返回的长整型字符串数组
const longIntStrArr = ["9007199254740993", "9007199254740992", "9007199254740995", "9007199254740994"];

function sortLongIntStrArr(arr, isAsc = true) {
  return [...arr].sort((a, b) => {
    // 转为字符串,避免数值精度丢失
    const strA = String(a);
    const strB = String(b);
    
    // 1. 比较长度:长度长的数值大
    if (strA.length !== strB.length) {
      const lenCompare = strA.length - strB.length;
      return isAsc ? lenCompare : -lenCompare;
    }
    
    // 2. 长度相同,按字典序比较
    for (let i = 0; i < strA.length; i++) {
      const charCompare = strA[i].charCodeAt(0) - strB[i].charCodeAt(0);
      if (charCompare !== 0) {
        return isAsc ? charCompare : -charCompare;
      }
    }
    
    // 两个数相等
    return 0;
  });
}

// 测试
console.log(sortLongIntStrArr(longIntStrArr)); 
// 输出:['9007199254740992','9007199254740993','9007199254740994','9007199254740995']
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# 使用BigInt类型进行比较(ES2020+支持)

核心逻辑: ES2020引入了 BigInt 类型,专门用于表示任意精度的整数,可精确表示长整型。我们可以将长整型转为 BigInt 后进行比较,避免精度丢失。

代码实现:

// 包含长整型的数组
const longIntArr = [9007199254740993n, 9007199254740992n, 9007199254740995n, 9007199254740994n];

// 用BigInt比较排序
function sortLongIntWithBigInt(arr, isAsc = true) {
  return [...arr].sort((a, b) => {
    const bigA = BigInt(a);
    const bigB = BigInt(b);
    // BigInt不能直接用减号运算,需通过比较返回-1、0、1
    if (bigA < bigB) return isAsc ? -1 : 1;
    if (bigA > bigB) return isAsc ? 1 : -1;
    return 0;
  });
}

// 测试
console.log(sortLongIntWithBigInt(longIntArr)); 
// 输出:[9007199254740992n, 9007199254740993n, 9007199254740994n, 9007199254740995n]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

注意事项:

  1. BigInt 兼容性:现代浏览器(Chrome 67+、Firefox 68+、Edge 79+)和Node.js 10.4+ 支持,若需兼容旧浏览器(如IE),不建议使用。
  2. BigInt 不能与 Number 直接混合运算,需显式转换类型。

# 四、方案对比与选择建议

方案 核心优势 局限性 推荐场景
后端返回字符串 避免类型转换 需协调后端调整接口 后端可控,可协商接口格式
BigInt类型 代码简洁,逻辑直观 兼容性有限,不支持旧浏览器 现代浏览器/Node.js环境,无需兼容旧版

# 五、总结与注意事项

JS不支持长整型排序的核心原因是 Number 类型的精度限制,解决问题的关键是避开直接的长整型数值运算,通过字符串比较或 BigInt 类型实现精确比较。

编辑 (opens new window)
#前端实现长整型排序
上次更新: 2026/01/21, 19:29:14
MySQL自增ID主键空洞
MySQL无感换表

← MySQL自增ID主键空洞 MySQL无感换表→

最近更新
01
订单超时取消
01-21
02
双 Token 登录
01-21
03
长短链接跳转
01-21
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式