Generate v4 UUID

JavaScript

这里有一段生成 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 + nn0~3 中的某一个数。最终得到的就是 8~11 的随机数(整数)。

最终,将生成的不超过 16 的整数转化成十六进制的对应字符。

第二部分的代码用到了正则匹配和替换(注意,这里 '4' 没有被替换,依然保留),主要不容易理解的是下面这个部分:

[1e7] + -1e3 + -4e3 + -8e3 + -1e11    

这里用到了 JavaScript 比较奇怪的类型转化功能。因为最开始是一个数组,所以这里的相加实际上是字符串的拼接。等价于 '10000000-1000-4000-8000-100000000000' 这个字符串。上面这样写主要是字符数上比较少。