
1. 前言做前端开发这几年随机数和随机 ID 算是天天打交道的东西。从生成验证码、抽奖活动到给数据打唯一标识、做前端埋点几乎每个项目里都能见到它们的影子。可就是这么基础的功能我在 Code Review 里见过太多翻车写法有些甚至直接导致线上事故。今天就把这些常见坑整理出来聊聊正确姿势。2. 随机数基础Math.random() 的边界问题先看最基础的。很多人写随机整数喜欢这样// 错误写法直接乘再取整 function getRandomInt(max) { return Math.floor(Math.random() * max); }这段代码看着没问题但有两个隐患。第一Math.random()返回的是 [0, 1) 区间的小数理论上永远取不到 1所以Math.floor(Math.random() * max)的结果范围是 0 到 max-1如果你期望的是 1 到 max那就踩坑了。第二当 max 特别大时浮点数精度会导致分布不均匀某些数字出现的概率会偏高。更稳妥的写法是// 推荐写法明确区间 function getRandomInt(min, max) { min Math.ceil(min); max Math.floor(max); return Math.floor(Math.random() * (max - min 1)) min; }这样写的好处是区间明确调用方一眼就能看出取值范围不容易产生歧义。3. 随机 ID 生成Date.now() 的并发陷阱随机 ID 是重灾区。我见过不少同学图省事直接用时间戳当 ID// 错误写法时间戳直接当 ID const id Date.now();这个写法在单用户、单次操作场景下勉强能用但一旦遇到并发请求比如用户快速点击提交按钮或者多个标签页同时操作两个请求落在同一毫秒内ID 就重复了。更别说分布式环境下多台服务器同时生成碰撞概率直线上升。稍微好一点的做法是时间戳加随机数// 改进写法时间戳 随机数拼接 function generateId() { return Date.now().toString(36) Math.random().toString(36).slice(2); }这个方案把时间戳转成 36 进制缩短长度再拼一段随机字符串碰撞概率大幅降低。但要注意它仍然不是严格唯一的只是工程上够用。4. 随机 ID 生成Math.random() 做唯一标识的隐患还有更偷懒的直接用Math.random()生成 ID// 错误写法纯随机数当唯一 ID const id Math.random().toString(36).slice(2);这个写法的问题在于Math.random()是伪随机数生成器它的种子是固定的算法理论上存在周期性。虽然实际使用中很难触发但在安全敏感场景比如生成优惠券码、重置密码的 token这种写法是绝对不行的因为可预测性太强。安全场景必须用密码学安全的随机数// 安全写法crypto.getRandomValues function secureRandomId() { const arr new Uint32Array(4); crypto.getRandomValues(arr); return Array.from(arr, v v.toString(16)).join(); }这里用crypto.getRandomValues()生成 4 个 32 位无符号整数转成十六进制拼接安全性比Math.random()高好几个量级。浏览器环境直接可用Node.js 里用crypto.randomBytes()效果类似。5. 随机 ID 生成UUID 的正确打开方式如果项目对 ID 唯一性要求高直接用 UUID 是最省心的。但很多人不知道crypto.randomUUID()已经是现代浏览器的标准 API 了// 推荐写法原生 UUID const id crypto.randomUUID(); // 输出示例3f2c1a8e-7b4d-4f6a-9c3e-2d5b8a1f0e6a这个 API 在 HTTPS 环境下可用兼容性方面Chrome 92、Firefox 95、Safari 15.4 都支持。如果项目需要兼容老浏览器可以退而求其次用第三方库比如uuid包但要注意它内部也是基于crypto.getRandomValues实现的。6. 业务中的典型错误场景说几个我实际见过的线上事故都是随机数用错导致的。第一个是抽奖活动。运营配置了中奖概率 10%开发用Math.random() 0.1判断是否中奖看起来没问题。但活动上线后运营反馈中奖率明显偏低。排查发现开发把随机数生成放在了循环外面导致所有用户共用同一个随机数第一批用户抽完后面的用户结果全被复用了。第二个是前端埋点。给每次点击生成一个事件 ID用Date.now()拼接结果用户快速连点同一毫秒内产生多个事件ID 全部重复后端去重时把正常数据也误删了。第三个是列表渲染的 key。用Math.random()当 key每次渲染都重新生成导致 React 无法正确识别元素出现输入框失焦、滚动位置错乱等诡异问题。key 应该用稳定且唯一的标识而不是随机数。7. 总结随机数和随机 ID 看着简单用错地方就是事故现场。总结几条经验生成随机整数明确最小值和最大值别用裸Math.random() * max。并发场景别用Date.now()当 ID至少拼上随机数。安全敏感场景比如 token、验证码必须用crypto.getRandomValues()。唯一性要求高直接用crypto.randomUUID()别自己造轮子。React 列表 key 用稳定 ID别用随机数。写代码之前先想清楚这个随机数用在什么场景是追求性能、追求唯一性还是追求安全性选对方案才能少踩坑。希望这篇文章能帮大家避开这些常见的坑写出更健壮的代码。