从一行JavaScript代码生成随机字符串说起

今天在写JS的时候,要生成一个随机字符串,JavaScript中没有PHP的uniqid()方法,我Google了一下,在Stack Overflow上看到了一个很漂亮的函数,只有一行:

function generateUIDNotMoreThan1million() {
    return ("0000" + (Math.random()*Math.pow(36,4) << 0).toString(36)).slice(-4)
}

这种代码以前都copy来直接用,不过今天读到一篇《从一行CSS调试代码中学到的JavaScript知识》,觉得还蛮有意思的,我也试着来解读一下这行代码。

在这里要生成的是一个4位字母数字混合的字符串,他把这个字符串看作了一个4位的36进制数字(10个数字+26个字母)。这个数字的上限用十进制表示是36的4次方。因此通过Math.random()*Math.pow(36,4)可以获得一个从0到36^4范围内的随机数字(带小数)。

然后使用<< 0截断这个数字的小数位。因为<<操作符会把操作数转换成整数,就像《从一行CSS》里的~~一样。

接下来使用.toString(36)将这个十进制整数转换成36进制。Number.prototype.toString方法接受一个2-36的整数参数,可以用来作进制转换。

然后"0000"+在左侧补全,再用slice(-4)截取右4位。这样可以在生成的字符串位数不够4位时在左侧补0。

这样就可以获得一个由数字和小写字母组成的4位随机字符串了。不过,我想生成的是7位字符串。

这很简单嘛,只要把4换成7就行了:

("0000000" + (Math.random()*Math.pow(36,7) << 0).toString(36)).slice(-7)

运行一次,诶?这输出的是什么:-7yajeh?为什么会有负号混进来?

把随机数换成小数试一下,按F12,在Console里输入

> ("0000000" + (0.2*(Math.pow(36,7)) << 0).toString(36)).slice(-7)
  "-ox90p9"

好奇怪的结果,出来负数了,看起来像是溢出了。

> 0.2*Math.pow(36,7)
  15672832819.2
> 15672832819.2<<0
  -1507036365

原来是按位左移搞出来的,按计算器看看。

果然是溢出了。按位左移后的结果会被当作二进制补码,最左边的一位是符号位。所以,超过2147483647的数字在按位左移之后会得到负数,而不仅仅是取整。那么,最多生成几位的随机字符串时,这种方法是安全的?答案是log36(2147483647)=5.996,5位。

想要获得更长的字符串怎么办?也很简单啦,只要用Math.floor代替<<取整就可以了。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注