如何生成一个不会重复随机数?
在 JavaScript 中,不存在绝对“永远不会重复”的纯随机数,但可通过“随机因子+唯一标识”组合生成全局唯一标识符(UUID) 或有序唯一 ID,实现“实际应用中永不重复”,以下是 3 种主流方案:
1. 最通用:生成 UUID(推荐)
UUID(如 UUID v4)通过随机数算法生成 128 位唯一字符串,重复概率极低(可忽略不计),无需依赖外部库,浏览器和 Node.js 均支持。
浏览器环境(原生 API)
现代浏览器(Chrome 89+、Firefox 91+ 等)支持 crypto.randomUUID() ,安全性和效率更高:
javascript:// 生成 UUID v4(格式:xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx)
const uniqueId = crypto.randomUUID();
console.log(uniqueId); // 示例:"a1b2c3d4-5678-49ab-cdef-1234567890ab"
兼容方案(含 Node.js)
若需兼容旧浏览器或在 Node.js 中使用,可自定义 UUID 生成函数:
javascript:function generateUUID() {// 利用随机数和固定规则生成 UUID v4return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {const r = Math.random() * 16 | 0;const v = c === 'x' ? r : (r & 0x3 | 0x8);return v.toString(16);});
}// 使用
const uniqueId = generateUUID();
console.log(uniqueId); // 示例:"3e4f5a6b-7c8d-4e5f-8a9b-0c1d2e3f4a5b"
2. 简单组合:时间戳+随机数+设备标识
利用“高精度时间戳(确保时间唯一性)+ 随机数(增加随机性)+ 设备信息(避免多设备冲突)”拼接,适合轻量场景。
javascript:function generateUniqueId() {// 1. 纳秒级时间戳(比毫秒更精确,避免同一毫秒重复)const timestamp = performance.now().toString().replace('.', ''); // 2. 4位随机数const random = Math.floor(Math.random() * 10000).toString().padStart(4, '0');// 3. 设备标识(简化版:取浏览器用户代理的哈希值)const deviceHash = btoa(navigator.userAgent).slice(0, 6); // 拼接生成唯一IDreturn `${timestamp}_${random}_${deviceHash}`;
}// 使用
const uniqueId = generateUniqueId();
console.log(uniqueId); // 示例:"1234567890123_4567_aBcDef"
3. 分布式场景:雪花算法(Snowflake)
若需在多服务/多设备环境中生成有序且唯一的 ID(含随机因子),可实现雪花算法(64 位整数),结构如下:
- 1 位符号位(固定 0,确保为正数);
- 41 位时间戳(毫秒级,可使用约 69 年);
- 10 位机器标识(如设备 ID 哈希,支持 1024 台设备);
- 12 位序列号(每台设备每毫秒可生成 4096 个唯一 ID)。
JavaScript 实现示例:class Snowflake {constructor(machineId = 1) {this.machineId = machineId & 0x3ff; // 机器ID限制为10位this.sequence = 0; // 序列号(0-4095)this.lastTimestamp = -1; // 上一次生成ID的时间戳}// 生成唯一IDnextId() {let timestamp = Date.now();// 处理时钟回拨(避免时间倒退导致重复)if (timestamp < this.lastTimestamp) throw new Error("时钟回拨,无法生成ID");// 同一毫秒内,递增序列号if (timestamp === this.lastTimestamp) {this.sequence = (this.sequence + 1) & 0xfff; // 限制为12位if (this.sequence === 0) timestamp = this.waitNextMs(); // 序列号用尽,等待下一毫秒} else {this.sequence = 0; // 新毫秒,重置序列号}this.lastTimestamp = timestamp;// 拼接64位ID:时间戳左移22位 + 机器ID左移12位 + 序列号return (((timestamp - 1609459200000) << 22) | // 时间戳偏移(以2021-01-01为起点)(this.machineId << 12) | this.sequence);}// 等待下一毫秒waitNextMs() {let timestamp = Date.now();while (timestamp <= this.lastTimestamp) timestamp = Date.now();return timestamp;}
}// 使用(指定机器ID为1,多设备需分配不同ID)
const snowflake = new Snowflake(1);
const uniqueId = snowflake.nextId();
console.log(uniqueId); // 示例:1234567890123456789(64位整数)
总结
日常场景:优先用 crypto.randomUUID() (浏览器)或自定义 UUID 函数,简单高效;
轻量场景:用“时间戳+随机数+设备标识”,无需复杂逻辑;
分布式/有序场景:用雪花算法,确保多设备无重复且 ID 有序。