这里有一段生成 UUID v4 非常短小的 JavaScript 代码。虽然有一些注释,但总体上并不是非常的好理解。
首先,用 TypeScript 翻译一遍原始的代码:
const slot = '10000000-1000-4000-8000-100000000000';
function rand(from: number, to: number) {
return Math.floor(Math.random() * (to - from + 1) + from);
}
function getRandomChar(input: number) {
const seed = input === 8 ? rand(8, 11) : rand(0, 15);
return seed.toString(16);
}
function uuidReplacer(char: string) {
return getRandomChar(+char);
}
function uuid() {
return slot.replace(/[018]/g, uuidReplacer);
}
接下来对原始的代码中的部分做一些解释:
a ^ Math.random() * 16 >> a/4
这个部分的代码不太好理解。首先,先根据运算符的优先级,给上面这个算式加上帮助理解的括号:
a ^ ( (Math.random() * 16) >> (a / 4) )
接下来,再来看每个部分都是什么意思:
Math.random() * 16
创建了一个 0~16
范围内的随机数,>> (a / 4)
这个运算之后,会得到几种不同的结果(注意,这里 a
只可能是 '0'
,'1'
,'8'
三种情况):
'0'
或'1'
的时候,结果是0~15
的随机数(整数)'8'
的时候,等价于>> 2
,所以结果是一个0~3
的随机数(整数)
最后一步异或运算 ^
,得到的可能结果分别是:
'0'
的时候,异或结果不变,是0~15
的随机数(整数)'1'
的时候,异或结果最后一个比特位的值正好相反,最终的结果仍然是0~15
的随机数(整数)'8'
的时候,最终结果是8~11
的随机数(整数)。因为8
的第四位二进制是1
其他都是0
,而0~3
这几个数的二进制位数不超过两位,所以位数之间不存在交集,异或运算相当于是8 + n
,n
是0~3
中的某一个数。最终得到的就是8~11
的随机数(整数)。
最终,将生成的不超过 16 的整数转化成十六进制的对应字符。
第二部分的代码用到了正则匹配和替换(注意,这里 '4'
没有被替换,依然保留),主要不容易理解的是下面这个部分:
[1e7] + -1e3 + -4e3 + -8e3 + -1e11
这里用到了 JavaScript 比较奇怪的类型转化功能。因为最开始是一个数组,所以这里的相加实际上是字符串的拼接。等价于 '10000000-1000-4000-8000-100000000000'
这个字符串。上面这样写主要是字符数上比较少。