不要再用 Math.random()!用這個 API 做到真隨機
當我們需要一個隨機數(shù)時,Math.random() 幾乎是所有人的第一反應。它簡單、直接,一行代碼就能得到一個 0 到 1 之間的浮點數(shù)。
然而,這個信手拈來的函數(shù),卻有著致命的缺陷。

Math.random() 的“原罪”:它是可預測的
Math.random() 生成的數(shù)字并非真正的隨機,而是偽隨機。
什么是偽隨機?它是由一個確定的算法,根據(jù)一個初始值(稱為“種子”)計算出來的一系列數(shù)字。這個算法本身是公開的,這意味著,如果你知道了初始的“種子”,你就能完全預測出接下來生成的每一個“隨機數(shù)”。
在早期的瀏覽器中,這個“種子”甚至可能只是簡單的時間戳,使得預測變得非常容易。雖然現(xiàn)代瀏覽器已經(jīng)改進了種子的生成方式,使其更難被猜測,但 Math.random() 的核心機制并沒有改變。ECMAScript 規(guī)范本身不要求 Math.random() 必須是密碼學安全的。
更安全的替代方案:crypto.getRandomValues()
window.crypto 是瀏覽器提供的一套用于密碼學操作的 API,而 crypto.getRandomValues() 就是其中的一員。它是一個密碼學安全偽隨機數(shù)生成器 (CSPRNG)。
與 Math.random() 不同,crypto.getRandomValues() 的設計目標就是提供密碼學級別的安全性。
1. 它是如何做到“真正隨機”的?
它直接從操作系統(tǒng)底層獲取高質(zhì)量的“熵 (Entropy)”。這些熵的來源是不可預測的物理事件,例如:
- 鼠標移動的精確時機和軌跡
- 鍵盤輸入的時機
- 硬件設備產(chǎn)生的微小噪聲
- 網(wǎng)絡數(shù)據(jù)包的到達時間
操作系統(tǒng)將這些不可預測的事件混合成一個“熵池”,crypto.getRandomValues() 正是從這個池中獲取隨機性,使其生成的數(shù)值在統(tǒng)計學上是真正不可預測的。
2. 如何使用 crypto.getRandomValues()?
它的用法與 Math.random() 有所不同。它不是直接返回一個數(shù)字,而是用于填充一個類型化數(shù)組 (Typed Array),如 Uint8Array 或 Uint32Array。
基礎用法:
// 創(chuàng)建一個包含 10 個字節(jié)的數(shù)組
const randomBytes = new Uint8Array(10);
// 用密碼學安全的隨機值填充它
crypto.getRandomValues(randomBytes);
console.log(randomBytes); // 輸出: Uint8Array(10) [185, 20, 248, 119, ...]這看起來似乎沒那么直觀,但別擔心,我們可以輕松地將它封裝成我們習慣使用的函數(shù)。
替代 Math.random() 的函數(shù):我們可以生成一個 32 位無符號整數(shù),然后將其轉(zhuǎn)換為 0 到 1 之間的浮點數(shù)。

生成范圍內(nèi)安全隨機整數(shù)的函數(shù)(常用):
function secureRandomInt(min, max) {
const range = max - min + 1;
// 創(chuàng)建一個足夠大的隨機數(shù),以減少模偏差
const randomValue = new Uint32Array(1);
crypto.getRandomValues(randomValue);
return min + (randomValue[0] % range);
}
console.log(secureRandomInt(1, 6)); // 模擬安全的骰子
console.log(secureRandomInt(1000, 9999)); // 生成一個安全的 4 位驗證碼Math.random() 適用于那些不涉及安全或公平性的應用場景,例如:
- 生成隨機的粒子效果、模擬下雨或下雪
- 創(chuàng)作隨機的圖案和視覺效果
- 需要玩家通過分享種子來玩到完全相同的游戲關卡
當需要真隨機時,請選擇 crypto.getRandomValues(),目前早已兼容各現(xiàn)代瀏覽器(IE 除外)。
































