技术原理概念解析
Math.random() 原理
Math.random()
函数返回一个范围在 0 - 1 的伪随机浮点数。为了保证足够的性能,它并不是实时生成随机数,而是直接生成一组 64 个随机数并放在缓存中。当这一组随机数取完之后,再重新生成一批放入缓存。其底层算法是公开的 xorshift128+
算法,在 V8 源码中可见。
Crypto.getRandomValues() 原理
Crypto.getRandomValues()
方法同样返回伪随机数。它的随机种子生成器更加无序,例如会利用系统层面的无序源(有些硬件自带随机种子)。并且该方法的底层实现没有缓存,随机数都是实时生成的。
适用场景和边界条件
Math.random() 适用场景和边界条件
- 适用场景 :在日常开发中,如果随机数仅用于普通的随机操作,如生成随机 ID、随机排序等,
Math.random()
是一个不错的选择,因为它性能高且实用。例如生成随机 ID 的代码:document.body.id = ('_' + Math.random()).replace('0.', '');
,随机排序的代码:[1, 2, 3, 4, 5].sort(_ => Math.random() - .5);
- 边界条件 :如果随机值与加密相关,或者涉及到金钱等安全性要求非常高的场景,不能使用
Math.random()
,因为它存在安全风险。例如抽奖活动若使用Math.random()
进行随机,攻击者有可能估算出一段时间内所有的中奖结果。
getRandomValues() 适用场景和边界条件
- 适用场景 :当需要实现加密操作,例如生成密钥,尤其是在 Node.js 服务层,或者随机值与安全和金钱相关时,必须使用
getRandomValues()
方法。 - 边界条件 :由于
getRandomValues()
方法性能比Math.random()
差,在高并发的场景下,如果随机数仅仅是用做随机,与安全和金钱不相关,不建议使用该方法。在 Web 前端,纯前端几乎不存在高并发情况,使用该方法的性能优势不明显。另外,在 IE11 浏览器下需要添加ms
私有前缀window.msCrypto.getRandomValues()
,IE10 不支持该方法。
相关技术方案优缺点对比
技术方案 | 优点 | 缺点 |
---|---|---|
Math.random() | 性能高,使用方便,适合普通随机场景 | 存在安全风险,不适合安全要求高的场景 |
getRandomValues() | 安全性高,适用于加密等安全要求高的场景 | 性能较差,方法名称长,记忆和使用不够敏捷 |
技术架构图/流程图
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;
A([开始]):::startend --> B{随机场景类型?}:::decision
B -->|普通随机| C[/使用 Math.random/]:::process
B -->|安全要求高| D[/使用 getRandomValues/]:::process
C --> E([结束]):::startend
D --> E
最佳实践
准则一:普通随机场景使用 Math.random()
- 原理说明 :
Math.random()
函数返回一个范围在 0 - 1 的伪随机浮点数,它会预先生成一组 64 个随机数放在缓存中,取完后再重新生成一批。这种方式性能较高,适合普通随机场景,因为在这些场景下对随机数的安全性要求不高。 - 实施步骤 :
- 在需要生成随机数的代码位置调用
Math.random()
函数。 - 根据具体需求对生成的随机数进行处理,例如生成随机 ID 可以将其转换为字符串并拼接;进行随机排序可以将其作为排序函数的比较参数。示例代码如下:
- 在需要生成随机数的代码位置调用
javascript
// 生成随机 ID
const randomId = ('_' + Math.random()).replace('0.', '');
console.log(randomId);
// 随机排序
const array = [1, 2, 3, 4, 5];
const sortedArray = array.sort(() => Math.random() - 0.5);
console.log(sortedArray);
- 常见误区警示 :不要将
Math.random()
用于与加密相关或涉及金钱等安全性要求非常高的场景,因为它存在安全风险,攻击者有可能估算出一段时间内的随机结果。 - 性能指标参考值 :
Math.random()
的性能较高,在普通随机场景下,其响应时间通常在毫秒级别,每秒可以生成大量的随机数。
准则二:安全要求高的场景使用 getRandomValues()
- 原理说明 :
Crypto.getRandomValues()
方法的随机种子生成器更加无序,会利用系统层面的无序源,并且随机数是实时生成的,没有缓存。这使得它生成的随机数安全性更高,适合加密等安全要求高的场景。 - 实施步骤 :
- 创建一个
Uint8Array
、Uint16Array
或Uint32Array
类型的数组,用于存储生成的随机数。 - 调用
Crypto.getRandomValues()
方法,并将数组作为参数传入。示例代码如下:
- 创建一个
javascript
// 创建一个长度为 16 的 Uint8Array 数组
const array = new Uint8Array(16);
// 生成随机数并存储在数组中
window.crypto.getRandomValues(array);
console.log(array);
- 常见误区警示 :由于
getRandomValues()
方法性能比Math.random()
差,在高并发的普通随机场景下不建议使用。另外,在 IE11 浏览器下需要添加ms
私有前缀window.msCrypto.getRandomValues()
,IE10 不支持该方法。 - 性能指标参考值 :
getRandomValues()
的性能相对较低,生成随机数的响应时间会比Math.random()
长,每秒生成的随机数数量也会相对较少。在实际应用中,需要根据具体的性能要求进行评估。
代码实验室
基础实现案例:使用 Math.random() 生成随机整数
- 代码块:
javascript
// 此代码使用 JavaScript 编写,版本要求为 ES6 及以上
// 定义一个函数,用于生成指定范围内的随机整数
function getRandomInt(min, max) {
// Math.random() 生成一个 0(包含)到 1(不包含)之间的随机浮点数
// 通过乘以 (max - min + 1) 并加上 min,将范围调整到 [min, max]
// Math.floor() 向下取整,确保结果为整数
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// 调用函数,生成 1 到 10 之间的随机整数
const randomInt = getRandomInt(1, 10);
console.log(randomInt);
- 环境要求说明:此代码可以在任何支持 JavaScript 的环境中运行,如浏览器的开发者工具控制台、Node.js 环境等。确保你的环境支持 ES6 及以上版本的 JavaScript。
- 预期输出示例 :控制台将输出一个 1 到 10 之间的随机整数,例如:
5
。 - 异常处理指南 :如果传入的
min
或max
不是有效的数字,可能会导致结果不符合预期。在调用函数时,确保传入的参数是有效的数字。
生产级优化方案案例:使用 getRandomValues() 生成安全的随机密码
- 代码块:
javascript
// 此代码使用 JavaScript 编写,版本要求为 ES6 及以上
// 定义一个函数,用于生成指定长度的安全随机密码
function generateSecurePassword(length) {
// 创建一个 Uint8Array 数组,用于存储随机数
const array = new Uint8Array(length);
// 使用 Crypto.getRandomValues() 生成随机数并存储在数组中
window.crypto.getRandomValues(array);
// 定义密码可能包含的字符集
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=';
let password = '';
// 遍历数组,将每个随机数映射到字符集中的一个字符
for (let i = 0; i < length; i++) {
password += characters[array[i] % characters.length];
}
return password;
}
// 调用函数,生成一个长度为 16 的安全随机密码
const securePassword = generateSecurePassword(16);
console.log(securePassword);
- 环境要求说明 :此代码需要在支持
Crypto.getRandomValues()
方法的环境中运行,如现代浏览器或 Node.js 环境。在 IE11 浏览器下需要添加ms
私有前缀window.msCrypto.getRandomValues()
,IE10 不支持该方法。 - 预期输出示例 :控制台将输出一个长度为 16 的安全随机密码,例如:
aB3@dE5#fG7*hI9$
。 - 异常处理指南 :如果浏览器不支持
Crypto.getRandomValues()
方法,代码将抛出错误。在使用该方法之前,可以先检查浏览器是否支持,例如:if ('crypto' in window && 'getRandomValues' in window.crypto) { /* 执行代码 */ }
。
延伸学习
为了帮助开发者更深入地学习 JavaScript 中 Math.random()
和 Crypto.getRandomValues()
生成随机数的相关技术,以下是一些权威的学习资源推荐:
官方文档章节
- MDN Web Docs:Math.random():MDN 是全球开发者共同维护的 Web 技术文档平台,提供了关于
Math.random()
的详细文档,包括语法、返回值、示例代码以及规范等内容。通过阅读该文档,开发者可以了解Math.random()
的基本用法和适用场景,同时也能了解到该方法的局限性,如不能提供密码学安全的随机数。 - MDN Web Docs:Crypto.getRandomValues():同样来自 MDN Web Docs,该文档详细介绍了
Crypto.getRandomValues()
方法。包括该方法的原理、参数、异常处理以及示例代码等。对于需要生成加密强度随机数的开发者来说,这是一个非常重要的参考文档。
经典论文/技术报告
- 基于 JavaScript 的 Math.random() 函数的研究:逻辑构建与深度探索:这篇文章深入探讨了 JavaScript 中
Math.random()
函数的逻辑构建,解析其工作原理,并对其进行深入研究。通过阅读该文章,开发者可以了解到Math.random()
函数背后的伪随机数生成器和工作机制,以及在实际应用中的场景和注意事项。 - 深入解析 crypto.getRandomValues():JavaScript 中的高安全性随机数生成:该文章详细解析了
crypto.getRandomValues()
方法,探讨其工作原理、使用方法以及适用场景。对于需要在密码学和其他安全要求较高的场景中使用随机数的开发者来说,这篇文章提供了很好的指导。
高质量开源项目
- Seedrandom:这是一个由 David Bau 开发的小巧但功能强大的 JavaScript 库,它允许程序员创建可重复的随机数序列。在许多场景中,能够控制随机性的能力是非常宝贵的,例如在测试、游戏开发或加密应用中。Seedrandom 提供了一个 API,通过种子(seed)生成随机数,给定相同的种子,它会返回完全一样的随机数序列。
- react-native-get-random-values:这是一个适用于 React Native 平台的开源项目,是对 Web 标准
crypto.getRandomValues
的精巧实现。该项目弥补了 React Native 环境中默认未完全集成crypto.getRandomValues
功能的空白,使得诸如uuid
这样的流行库能在 React Native 应用中无缝工作,保证生成唯一标识符等场景下的数据安全性。
进阶技术路线图
- 在掌握了
Math.random()
和Crypto.getRandomValues()
的基本用法后,你可以进一步学习 Web Crypto API 的其他功能,如加密、解密、签名和验证等。可以参考 MDN Web Docs 上的 Web Cryptography API 文档进行深入学习。 - 对于需要在 Node.js 环境中使用加密功能的,可以学习 Node.js 的
crypto
模块。可以参考 Node.js v23 文档:crypto 加密 了解该模块的详细用法。