今天在写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
代替<<
取整就可以了。