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

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

生成范圍內(nèi)安全隨機(jī)整數(shù)的函數(shù)(常用):
function secureRandomInt(min, max) {
const range = max - min + 1;
// 創(chuàng)建一個足夠大的隨機(jī)數(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() 適用于那些不涉及安全或公平性的應(yīng)用場景,例如:
- 生成隨機(jī)的粒子效果、模擬下雨或下雪
- 創(chuàng)作隨機(jī)的圖案和視覺效果
- 需要玩家通過分享種子來玩到完全相同的游戲關(guān)卡
當(dāng)需要真隨機(jī)時,請選擇 crypto.getRandomValues(),目前早已兼容各現(xiàn)代瀏覽器(IE 除外)。

































