我正在尝试在JavaScript中创建全局唯一标识符。我不确定所有浏览器上都提供哪些例程,内置随机数生成器的“随机性”和种子的播种方式等等。
GUID / UUID至少应包含32个字符,并且应保持在ASCII范围内,以免在传递它们时遇到麻烦。
我正在尝试在JavaScript中创建全局唯一标识符。我不确定所有浏览器上都提供哪些例程,内置随机数生成器的“随机性”和种子的播种方式等等。
GUID / UUID至少应包含32个字符,并且应保持在ASCII范围内,以免在传递它们时遇到麻烦。
Answers:
根据RFC 4122,UUID(通用唯一IDentifier),也称为GUID(全局唯一IDentifier),是旨在提供某些唯一性保证的标识符。
虽然可以在几行JS中实现符合RFC的UUID(例如,请参见下面的@broofa的答案),但有一些常见的陷阱:
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
”,其中x是一个[0-9,AF] 中号是[1-5]中的一个,和Ñ是[8,9,一个,或b]Math.random
)因此,鼓励为生产环境编写代码的开发人员使用严格的,维护良好的实现,例如uuid模块。
对于符合RFC4122版本4的解决方案,此一站式解决方案是我能想到的最紧凑的解决方案:
function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
console.log(uuidv4());
更新,2015-06-02:请注意,UUID的唯一性很大程度上取决于基础随机数生成器(RNG)。上面的解决方案Math.random()
出于简洁Math.random()
目的而使用,但是不能保证是高质量的RNG。有关详细信息,请参见Adam Hyland 在Math.random()上的出色文章。对于更强大的解决方案,请考虑使用uuid模块,该模块使用了更高质量的RNG API。
更新,2015-08-26:作为一个旁注,本要点描述了如何确定在达到一定的碰撞概率之前可以生成多少个ID。例如,使用3.26x10 15版本4 RFC4122 UUID,您有百万分之一的碰撞机会。
更新,2017-06-28:Chrome开发人员的一篇不错的文章,讨论了Chrome,Firefox和Safari中Math.random PRNG的质量状态。tl; dr-截至2015年末,它的“相当好”,但没有加密质量。为了解决该问题,这是上述解决方案的更新版本,其中使用了ES6,crypto
API和一些JS向导,我对此不以为然:
function uuidv4() {
return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
);
}
console.log(uuidv4());
2020年1月6日更新:工作中有一项建议,建议将标准uuid
模块作为JS语言的一部分
c== 'x'
代替c === 'x'
。因为jshint失败。
我真的很喜欢Broofa的答案有多干净,但是不幸的是,糟糕的实现Math.random
留下了碰撞的机会。
这是一个类似的RFC4122版本4兼容解决方案,它通过将时间戳记的十六进制部分偏移前13个十六进制数字,以及自页面加载以来一次偏移了微秒的十六进制部分来解决该问题。这样,即使Math.random
位于同一种子上,两个客户端也必须自页面加载以来(如果支持高性能时间),以完全相同的毫秒数(或10,000+年后)生成UUID。得到相同的UUID:
function generateUUID() { // Public Domain/MIT
var d = new Date().getTime();//Timestamp
var d2 = (performance && performance.now && (performance.now()*1000)) || 0;//Time in microseconds since page-load or 0 if unsupported
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16;//random number between 0 and 16
if(d > 0){//Use timestamp until depleted
r = (d + r)%16 | 0;
d = Math.floor(d/16);
} else {//Use microseconds since page-load if supported
r = (d2 + r)%16 | 0;
d2 = Math.floor(d2/16);
}
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
});
}
console.log(generateUUID())
new Date().getTime()
不是每毫秒更新一次。我不确定这会如何影响您算法的预期随机性。
d = Math.floor(d/16);
什么吗?
broofa的答案确实很巧妙-确实非常聪明,确实...符合rfc4122的要求,具有一定的可读性和紧凑性。太棒了!
但是,如果您正在查看该正则表达式,这么多的replace()
回调,toString()
的和Math.random()
函数调用(其中他仅使用4位结果,而浪费了其余部分),则您可能会开始怀疑性能。确实,joelpt甚至决定使用放弃RFC,以实现通用GUID速度generateQuickGUID
。
但是,我们可以获得速度和 RFC合规性吗?我说是! 我们可以保持可读性吗?好吧...并非如此,但是如果您继续这样做很容易。
但是首先,与broofa guid
(接受的答案)和不符合rfc的结果相比,我的结果是generateQuickGuid
:
Desktop Android
broofa: 1617ms 12869ms
e1: 636ms 5778ms
e2: 606ms 4754ms
e3: 364ms 3003ms
e4: 329ms 2015ms
e5: 147ms 1156ms
e6: 146ms 1035ms
e7: 105ms 726ms
guid: 962ms 10762ms
generateQuickGuid: 292ms 2961ms
- Note: 500k iterations, results will vary by browser/cpu.
所以,我的优化迭代6日,我通过在击败了最受欢迎的答案是12倍,超过公认的答案9X,并通过非依从快速应答,2-3X。而且我仍然兼容rfc4122。
有兴趣如何?我将完整的源代码放在http://jsfiddle.net/jcward/7hyaC/3/和http://jsperf.com/uuid-generator-opt/4上
作为解释,让我们从broofa的代码开始:
function broofa() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
return v.toString(16);
});
}
console.log(broofa())
因此,它x
用任意随机的十六进制数字替换,并替换为y
随机数据(10
根据RFC规范将前2位强制为),并且正则表达式与-
或4
字符不匹配,因此他不必处理它们。非常非常光滑
首先要知道的是,函数调用和正则表达式一样昂贵(尽管他仅使用1,但它具有32个回调,每个匹配项一个,在32个回调中的每个回调中,它调用Math.random()和v。 toString(16))。
迈向性能的第一步是消除RegEx及其回调函数,而使用简单的循环。这意味着我们必须处理-
和4
字符,而broofa则不需要。另外,请注意,我们可以使用字符串数组索引来保持其精巧的String模板体系结构:
function e1() {
var u='',i=0;
while(i++<36) {
var c='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'[i-1],r=Math.random()*16|0,v=c=='x'?r:(r&0x3|0x8);
u+=(c=='-'||c=='4')?c:v.toString(16)
}
return u;
}
console.log(e1())
基本上,相同的内部逻辑,除了我们检查-
或4
,并使用while循环(而不是replace()
回调)使我们几乎提高了3倍!
下一步是台式机上的一小步,但在移动设备上将有很大的不同。让我们进行更少的Math.random()调用,并利用所有这些随机位,而不是使用随机缓冲区将其丢弃87%,该缓冲区在每次迭代中都移出。让我们也将该模板定义移出循环,以防万一它有帮助:
function e2() {
var u='',m='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx',i=0,rb=Math.random()*0xffffffff|0;
while(i++<36) {
var c=m[i-1],r=rb&0xf,v=c=='x'?r:(r&0x3|0x8);
u+=(c=='-'||c=='4')?c:v.toString(16);rb=i%8==0?Math.random()*0xffffffff|0:rb>>4
}
return u
}
console.log(e2())
根据平台的不同,这可以为我们节省10-30%。不错。但是下一步要走的是toString函数调用以及优化经典方法-查找表。一个简单的16元素查找表将以更少的时间执行toString(16)的工作:
function e3() {
var h='0123456789abcdef';
var k='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
/* same as e4() below */
}
function e4() {
var h=['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'];
var k=['x','x','x','x','x','x','x','x','-','x','x','x','x','-','4','x','x','x','-','y','x','x','x','-','x','x','x','x','x','x','x','x','x','x','x','x'];
var u='',i=0,rb=Math.random()*0xffffffff|0;
while(i++<36) {
var c=k[i-1],r=rb&0xf,v=c=='x'?r:(r&0x3|0x8);
u+=(c=='-'||c=='4')?c:h[v];rb=i%8==0?Math.random()*0xffffffff|0:rb>>4
}
return u
}
console.log(e4())
下一个优化是另一个经典。由于我们在每次循环迭代中仅处理4位输出,因此让我们将循环数减少一半,并在每次迭代中处理8位。这很棘手,因为我们仍然必须处理RFC兼容位的位置,但这并不难。然后,我们必须制作一个更大的查找表(16x16或256)来存储0x00-0xff,并且只在e5()函数外部构建一次。
var lut = []; for (var i=0; i<256; i++) { lut[i] = (i<16?'0':'')+(i).toString(16); }
function e5() {
var k=['x','x','x','x','-','x','x','-','4','x','-','y','x','-','x','x','x','x','x','x'];
var u='',i=0,rb=Math.random()*0xffffffff|0;
while(i++<20) {
var c=k[i-1],r=rb&0xff,v=c=='x'?r:(c=='y'?(r&0x3f|0x80):(r&0xf|0x40));
u+=(c=='-')?c:lut[v];rb=i%4==0?Math.random()*0xffffffff|0:rb>>8
}
return u
}
console.log(e5())
我尝试了一次可处理16位的e6(),但仍使用256个元素的LUT,它显示出优化收益递减。尽管迭代次数较少,但内部逻辑却因处理量的增加而变得复杂,并且在台式机上的性能相同,而在移动设备上的速度仅快10%。
要应用的最终优化技术-展开循环。由于我们要循环固定的次数,因此从技术上讲,我们可以手动将其全部写出。我用一个随机变量r进行了一次尝试,我一直重新分配它,并降低了性能。但是先给四个变量分配了随机数据,然后使用查找表并应用适当的RFC位,此版本将它们全部清除:
var lut = []; for (var i=0; i<256; i++) { lut[i] = (i<16?'0':'')+(i).toString(16); }
function e7()
{
var d0 = Math.random()*0xffffffff|0;
var d1 = Math.random()*0xffffffff|0;
var d2 = Math.random()*0xffffffff|0;
var d3 = Math.random()*0xffffffff|0;
return lut[d0&0xff]+lut[d0>>8&0xff]+lut[d0>>16&0xff]+lut[d0>>24&0xff]+'-'+
lut[d1&0xff]+lut[d1>>8&0xff]+'-'+lut[d1>>16&0x0f|0x40]+lut[d1>>24&0xff]+'-'+
lut[d2&0x3f|0x80]+lut[d2>>8&0xff]+'-'+lut[d2>>16&0xff]+lut[d2>>24&0xff]+
lut[d3&0xff]+lut[d3>>8&0xff]+lut[d3>>16&0xff]+lut[d3>>24&0xff];
}
console.log(e7())
Modualized: http://jcward.com/UUID.js -UUID.generate()
有趣的是,轻松生成16个字节的随机数据。整个技巧是用符合RFC的String格式表示它,而最紧密地实现是使用16个字节的随机数据,展开的循环和查找表。
我希望我的逻辑是正确的-在这种繁琐的工作中容易犯错。但是输出对我来说看起来不错。希望您喜欢这段疯狂的代码优化之旅!
忠告:我的主要目标是展示和教授潜在的优化策略。其他答案涵盖重要主题,例如冲突和真正的随机数,这对于生成良好的UUID至关重要。
Math.random()*0xFFFFFFFF
行应该是Math.random()*0x100000000
完全随机的,并且>>>0
应该用来代替|0
使这些值保持无符号(尽管对于当前代码,我认为即使它们是有符号的,它也可以实现)。最后,如果可以使用的window.crypto.getRandomValues
话,这几天将是一个非常好的主意,只有在绝对必要时才使用Math.random。Math.random可能只有少于128位的熵,在这种情况下,它比必要时更容易受到冲突的影响。
这是一些基于RFC 4122第4.4节(根据真正随机数或伪随机数创建UUID的算法)的代码。
function createUUID() {
// http://www.ietf.org/rfc/rfc4122.txt
var s = [];
var hexDigits = "0123456789abcdef";
for (var i = 0; i < 36; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
}
s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
s[8] = s[13] = s[18] = s[23] = "-";
var uuid = s.join("");
return uuid;
}
var s = new Array(36);
let uniqueId = Math.random().toString(36).substring(2) + Date.now().toString(36);
如果ID的生成间隔超过1毫秒,则它们是100%唯一的。
如果以较短的时间间隔生成两个ID,并且假设随机方法是真正随机的,则这将生成99.99999999999999%的ID可能是全局唯一的(冲突10 of 15中的1个)
您可以通过添加更多数字来增加此数字,但是要生成100%的唯一ID,您将需要使用全局计数器。
如果您需要RFC兼容性,此格式将作为有效的版本4 GUID传递:
let u = Date.now().toString(16) + Math.random().toString(16) + '0'.repeat(16);
let guid = [u.substr(0,8), u.substr(8,4), '4000-8' + u.substr(13,3), u.substr(16,12)].join('-');
编辑:上面的代码遵循意图,但不是RFC的字母。除其他差异外,还有一些随机数短。(如果需要,可以添加更多随机数字)好处是,它的速度非常快:)您可以在此处测试GUID的有效性
[slug, date, random].join("_")
要创建usr_1dcn27itd_hj6onj6phr
。它使id也是“ created at”字段的
最快的GUID格式的字符串生成器方法XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
。这不会生成符合标准的GUID。
此实现的一千万次执行仅需32.5秒,这是我在浏览器中见过的最快速度(这是唯一一种没有循环/迭代的解决方案)。
该函数很简单:
/**
* Generates a GUID string.
* @returns {string} The generated GUID.
* @example af8a8416-6e18-a307-bd9c-f2c947bbb3aa
* @author Slavik Meltser.
* @link http://slavik.meltser.info/?p=142
*/
function guid() {
function _p8(s) {
var p = (Math.random().toString(16)+"000000000").substr(2,8);
return s ? "-" + p.substr(0,4) + "-" + p.substr(4,4) : p ;
}
return _p8() + _p8(true) + _p8(true) + _p8();
}
要测试性能,可以运行以下代码:
console.time('t');
for (var i = 0; i < 10000000; i++) {
guid();
};
console.timeEnd('t');
我敢肯定你们中的大多数人都会明白我在那里所做的事情,但是也许至少有一个人需要解释:
算法:
Math.random()
函数返回一个介于0和1之间的十进制数字,该数字在小数点后是16位(例如0.4363923368509859
)。0.6fb7687f
)。Math.random().toString(16)
。0.
前缀(0.6fb7687f
=>
6fb7687f
),并得到一个长度为八个十六进制字符的字符串。(Math.random().toString(16).substr(2,8)
。Math.random()
函数将返回较短的数字(例如0.4363
)(从上面的示例中,实际上是0.4363000000000000
)。这就是为什么我要追加到此字符串"000000000"
(具有九个零的字符串),然后使用substr()
函数将其截断以使其准确地成为九个字符(在右侧填充零)的原因。Math.random()
函数将恰好返回0或1时(每个概率为1/10 ^ 16)。这就是为什么我们需要为其添加9个零("0"+"000000000"
或"1"+"000000000"
),然后将其与长度为8个字符的第二个索引(第3个字符)相去。在其他情况下,加零不会损害结果,因为无论如何它都会切断结果。Math.random().toString(16)+"000000000").substr(2,8)
。组装:
XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
。XXXXXXXX
和-XXXX-XXXX
。XXXXXXXX
-XXXX-XXXX
-XXXX-XXXX
XXXXXXXX
。_p8(s)
,该s
参数告诉该函数是否添加破折号。_p8() + _p8(true) + _p8(true) + _p8()
并将其返回。请享用!:-)
以下是最受好评的答案的组合,以及针对Chrome浏览器冲突的解决方法:
generateGUID = (typeof(window.crypto) != 'undefined' &&
typeof(window.crypto.getRandomValues) != 'undefined') ?
function() {
// If we have a cryptographically secure PRNG, use that
// /programming/6906916/collisions-when-generating-uuids-in-javascript
var buf = new Uint16Array(8);
window.crypto.getRandomValues(buf);
var S4 = function(num) {
var ret = num.toString(16);
while(ret.length < 4){
ret = "0"+ret;
}
return ret;
};
return (S4(buf[0])+S4(buf[1])+"-"+S4(buf[2])+"-"+S4(buf[3])+"-"+S4(buf[4])+"-"+S4(buf[5])+S4(buf[6])+S4(buf[7]));
}
:
function() {
// Otherwise, just use Math.random
// /programming/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
return v.toString(16);
});
};
如果要测试,请在jsbin上进行。
, does not keep the Version 4 UUIDs format defined by RFC 4122. That is instead of
xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx` xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
。
这是一个完全不合规但非常有效的实现,用于生成类似ASCII安全的GUID的唯一标识符。
function generateQuickGuid() {
return Math.random().toString(36).substring(2, 15) +
Math.random().toString(36).substring(2, 15);
}
生成26个[a-z0-9]字符,生成的UID比符合RFC的GUID短且唯一。如果人类可读性很重要,则可以添加短划线。
这是此功能的使用示例和时间安排,以及该问题的其他一些答案。计时是在Chrome m25下执行的,每次进行1000万次迭代。
>>> generateQuickGuid()
"nvcjf1hs7tf8yyk4lmlijqkuo9"
"yq6gipxqta4kui8z05tgh9qeel"
"36dh5sec7zdj90sk2rx7pjswi2"
runtime: 32.5s
>>> GUID() // John Millikin
"7a342ca2-e79f-528e-6302-8f901b0b6888"
runtime: 57.8s
>>> regexGuid() // broofa
"396e0c46-09e4-4b19-97db-bd423774a4b3"
runtime: 91.2s
>>> createUUID() // Kevin Hakanson
"403aa1ab-9f70-44ec-bc08-5d5ac56bd8a5"
runtime: 65.9s
>>> UUIDv4() // Jed Schmidt
"f4d7d31f-fa83-431a-b30c-3e6cc37cc6ee"
runtime: 282.4s
>>> Math.uuid() // broofa
"5BD52F55-E68F-40FC-93C2-90EE069CE545"
runtime: 225.8s
>>> Math.uuidFast() // broofa
"6CB97A68-23A2-473E-B75B-11263781BBE6"
runtime: 92.0s
>>> Math.uuidCompact() // broofa
"3d7b7a06-0a67-4b67-825c-e5c43ff8c1e8"
runtime: 229.0s
>>> bitwiseGUID() // jablko
"baeaa2f-7587-4ff1-af23-eeab3e92"
runtime: 79.6s
>>>> betterWayGUID() // Andrea Turri
"383585b0-9753-498d-99c3-416582e9662c"
runtime: 60.0s
>>>> UUID() // John Fowler
"855f997b-4369-4cdb-b7c9-7142ceaf39e8"
runtime: 62.2s
这是时间码。
var r;
console.time('t');
for (var i = 0; i < 10000000; i++) {
r = FuncToTest();
};
console.timeEnd('t');
这是日期为2011年10月9日的解决方案,来自用户jed在https://gist.github.com/982883的评论:
UUIDv4 = function b(a){return a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,b)}
这可以达到与当前最高评分答案相同的目标,但是通过利用强制,递归和指数表示法,可以减少50多个字节。对于那些好奇它如何工作的人,下面是该函数旧版本的带注释的形式:
UUIDv4 =
function b(
a // placeholder
){
return a // if the placeholder was passed, return
? ( // a random number from 0 to 15
a ^ // unless b is 8,
Math.random() // in which case
* 16 // a random number from
>> a/4 // 8 to 11
).toString(16) // in hexadecimal
: ( // or otherwise a concatenated string:
[1e7] + // 10000000 +
-1e3 + // -1000 +
-4e3 + // -4000 +
-8e3 + // -80000000 +
-1e11 // -100000000000,
).replace( // replacing
/[018]/g, // zeroes, ones, and eights with
b // random hex digits
)
}
function generateGuid() {
var result, i, j;
result = '';
for(j=0; j<32; j++) {
if( j == 8 || j == 12 || j == 16 || j == 20)
result = result + '-';
i = Math.floor(Math.random()*16).toString(16).toUpperCase();
result = result + i;
}
return result;
}
还有其他涉及使用ActiveX控件的方法,但请远离这些方法!
编辑: 我认为值得指出的是,没有GUID生成器可以保证唯一键(请参阅Wikipedia文章)。总是有碰撞的机会。GUID仅提供足够大的键范围,以将冲突的变化减少到几乎为零。
您可以使用node-uuid(https://github.com/kelektiv/node-uuid)
简单,快速生成RFC4122 UUIDS。
特征:
使用NPM安装:
npm install uuid
或通过浏览器使用uuid:
下载原始文件(uuid v1):https : //raw.githubusercontent.com/kelektiv/node-uuid/master/v1.js 下载原始文件(uuid v4):https : //raw.githubusercontent.com/kelektiv/node -uuid / master / v4.js
想要更小?看看这个:https : //gist.github.com/jed/982883
用法:
// Generate a v1 UUID (time-based)
const uuidV1 = require('uuid/v1');
uuidV1(); // -> '6c84fb90-12c4-11e1-840d-7b25c5ee775a'
// Generate a v4 UUID (random)
const uuidV4 = require('uuid/v4');
uuidV4(); // -> '110ec58a-a0f2-4ac4-8393-c866d813b8d1'
// Generate a v5 UUID (namespace)
const uuidV5 = require('uuid/v5');
// ... using predefined DNS namespace (for domain names)
uuidV5('hello.example.com', v5.DNS)); // -> 'fdda765f-fc57-5604-a269-52a7df8164ec'
// ... using predefined URL namespace (for, well, URLs)
uuidV5('http://example.com/hello', v5.URL); // -> '3bbcee75-cecc-5b56-8031-b6641c1ed1f1'
// ... using a custom namespace
const MY_NAMESPACE = '(previously generated unique uuid string)';
uuidV5('hello', MY_NAMESPACE); // -> '90123e1c-7512-523e-bb28-76fab9f2f73d'
ES6:
import uuid from 'uuid/v4';
const id = uuid();
var uuid = function() {
var buf = new Uint32Array(4);
window.crypto.getRandomValues(buf);
var idx = -1;
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
idx++;
var r = (buf[idx>>3] >> ((idx%8)*4))&15;
var v = c == 'x' ? r : (r&0x3|0x8);
return v.toString(16);
});
};
编辑:
重新访问了正在使用此功能的项目,但不喜欢冗长的内容。-但是需要适当的随机性。
一个基于Briguy37的答案和一些按位运算符的版本,用于从缓冲区中提取半字节大小的窗口。
由于我上次使用Java的UUID解析不兼容的uuid时遇到问题,因此应该遵循RFC 4类(随机)模式。
简单的JavaScript模块是该线程中最佳答案的组合。
var crypto = window.crypto || window.msCrypto || null; // IE11 fix
var Guid = Guid || (function() {
var EMPTY = '00000000-0000-0000-0000-000000000000';
var _padLeft = function(paddingString, width, replacementChar) {
return paddingString.length >= width ? paddingString : _padLeft(replacementChar + paddingString, width, replacementChar || ' ');
};
var _s4 = function(number) {
var hexadecimalResult = number.toString(16);
return _padLeft(hexadecimalResult, 4, '0');
};
var _cryptoGuid = function() {
var buffer = new window.Uint16Array(8);
window.crypto.getRandomValues(buffer);
return [_s4(buffer[0]) + _s4(buffer[1]), _s4(buffer[2]), _s4(buffer[3]), _s4(buffer[4]), _s4(buffer[5]) + _s4(buffer[6]) + _s4(buffer[7])].join('-');
};
var _guid = function() {
var currentDateMilliseconds = new Date().getTime();
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(currentChar) {
var randomChar = (currentDateMilliseconds + Math.random() * 16) % 16 | 0;
currentDateMilliseconds = Math.floor(currentDateMilliseconds / 16);
return (currentChar === 'x' ? randomChar : (randomChar & 0x7 | 0x8)).toString(16);
});
};
var create = function() {
var hasCrypto = crypto != 'undefined' && crypto !== null,
hasRandomValues = typeof(window.crypto.getRandomValues) != 'undefined';
return (hasCrypto && hasRandomValues) ? _cryptoGuid() : _guid();
};
return {
newGuid: create,
empty: EMPTY
};
})();
// DEMO: Create and show GUID
console.log(Guid.newGuid());
用法:
Guid.newGuid()
“ c6c2d12f-d76b-5739-e551-07e6de5b0807”
空的
“ 00000000-0000-0000-0000-000000000000”
GUID
作为string
。你的回答至少铲断很多更高效的存储使用Uint16Array
。该toString
功能应该是使用二进制表示在JavaScript的object
此创建版本4 UUID(从伪随机数创建):
function uuid()
{
var chars = '0123456789abcdef'.split('');
var uuid = [], rnd = Math.random, r;
uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
uuid[14] = '4'; // version 4
for (var i = 0; i < 36; i++)
{
if (!uuid[i])
{
r = 0 | rnd()*16;
uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r & 0xf];
}
}
return uuid.join('');
}
这是生成的UUID的示例:
682db637-0f31-4847-9cdf-25ba9613a75c
97d19478-3ab2-4aa1-b8cc-a1c3540f54aa
2eed04c9-2692-456d-a0fd-51012f947136
嗯,这已经有了很多答案,但是不幸的是,这还没有一个“真正的”随机变量。下面的版本是对broofa答案的改编,但已更新为包括“真”随机函数,该函数使用可用的加密库,以及Alea()函数作为后备。
Math.log2 = Math.log2 || function(n){ return Math.log(n) / Math.log(2); }
Math.trueRandom = (function() {
var crypt = window.crypto || window.msCrypto;
if (crypt && crypt.getRandomValues) {
// if we have a crypto library, use it
var random = function(min, max) {
var rval = 0;
var range = max - min;
if (range < 2) {
return min;
}
var bits_needed = Math.ceil(Math.log2(range));
if (bits_needed > 53) {
throw new Exception("We cannot generate numbers larger than 53 bits.");
}
var bytes_needed = Math.ceil(bits_needed / 8);
var mask = Math.pow(2, bits_needed) - 1;
// 7776 -> (2^13 = 8192) -1 == 8191 or 0x00001111 11111111
// Create byte array and fill with N random numbers
var byteArray = new Uint8Array(bytes_needed);
crypt.getRandomValues(byteArray);
var p = (bytes_needed - 1) * 8;
for(var i = 0; i < bytes_needed; i++ ) {
rval += byteArray[i] * Math.pow(2, p);
p -= 8;
}
// Use & to apply the mask and reduce the number of recursive lookups
rval = rval & mask;
if (rval >= range) {
// Integer out of acceptable range
return random(min, max);
}
// Return an integer that falls within the range
return min + rval;
}
return function() {
var r = random(0, 1000000000) / 1000000000;
return r;
};
} else {
// From https://web.archive.org/web/20120502223108/http://baagoe.com/en/RandomMusings/javascript/
// Johannes Baagøe <baagoe@baagoe.com>, 2010
function Mash() {
var n = 0xefc8249d;
var mash = function(data) {
data = data.toString();
for (var i = 0; i < data.length; i++) {
n += data.charCodeAt(i);
var h = 0.02519603282416938 * n;
n = h >>> 0;
h -= n;
h *= n;
n = h >>> 0;
h -= n;
n += h * 0x100000000; // 2^32
}
return (n >>> 0) * 2.3283064365386963e-10; // 2^-32
};
mash.version = 'Mash 0.9';
return mash;
}
// From http://baagoe.com/en/RandomMusings/javascript/
function Alea() {
return (function(args) {
// Johannes Baagøe <baagoe@baagoe.com>, 2010
var s0 = 0;
var s1 = 0;
var s2 = 0;
var c = 1;
if (args.length == 0) {
args = [+new Date()];
}
var mash = Mash();
s0 = mash(' ');
s1 = mash(' ');
s2 = mash(' ');
for (var i = 0; i < args.length; i++) {
s0 -= mash(args[i]);
if (s0 < 0) {
s0 += 1;
}
s1 -= mash(args[i]);
if (s1 < 0) {
s1 += 1;
}
s2 -= mash(args[i]);
if (s2 < 0) {
s2 += 1;
}
}
mash = null;
var random = function() {
var t = 2091639 * s0 + c * 2.3283064365386963e-10; // 2^-32
s0 = s1;
s1 = s2;
return s2 = t - (c = t | 0);
};
random.uint32 = function() {
return random() * 0x100000000; // 2^32
};
random.fract53 = function() {
return random() +
(random() * 0x200000 | 0) * 1.1102230246251565e-16; // 2^-53
};
random.version = 'Alea 0.9';
random.args = args;
return random;
}(Array.prototype.slice.call(arguments)));
};
return Alea();
}
}());
Math.guid = function() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.trueRandom() * 16 | 0,
v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
};
GitHub上的JavaScript项目-https: //github.com/LiosK/UUID.js
UUID.js JavaScript的RFC兼容UUID生成器。
参见RFC 4122 http://www.ietf.org/rfc/rfc4122.txt。
功能生成符合RFC 4122的UUID。
提供版本4 UUID(来自随机数的UUID)和版本1 UUID(基于时间的UUID)。
UUID对象允许对UUID的各种访问,包括对UUID字段的访问。
JavaScript的低时间戳分辨率可以通过随机数来补偿。
// RFC 4122
//
// A UUID is 128 bits long
//
// String representation is five fields of 4, 2, 2, 2, and 6 bytes.
// Fields represented as lowercase, zero-filled, hexadecimal strings, and
// are separated by dash characters
//
// A version 4 UUID is generated by setting all but six bits to randomly
// chosen values
var uuid = [
Math.random().toString(16).slice(2, 10),
Math.random().toString(16).slice(2, 6),
// Set the four most significant bits (bits 12 through 15) of the
// time_hi_and_version field to the 4-bit version number from Section
// 4.1.3
(Math.random() * .0625 /* 0x.1 */ + .25 /* 0x.4 */).toString(16).slice(2, 6),
// Set the two most significant bits (bits 6 and 7) of the
// clock_seq_hi_and_reserved to zero and one, respectively
(Math.random() * .25 /* 0x.4 */ + .5 /* 0x.8 */).toString(16).slice(2, 6),
Math.random().toString(16).slice(2, 14)].join('-');
我想了解broofa的答案,因此我对其进行了扩展并添加了评论:
var uuid = function () {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(
/[xy]/g,
function (match) {
/*
* Create a random nibble. The two clever bits of this code:
*
* - Bitwise operations will truncate floating point numbers
* - For a bitwise OR of any x, x | 0 = x
*
* So:
*
* Math.random * 16
*
* creates a random floating point number
* between 0 (inclusive) and 16 (exclusive) and
*
* | 0
*
* truncates the floating point number into an integer.
*/
var randomNibble = Math.random() * 16 | 0;
/*
* Resolves the variant field. If the variant field (delineated
* as y in the initial string) is matched, the nibble must
* match the mask (where x is a do-not-care bit):
*
* 10xx
*
* This is achieved by performing the following operations in
* sequence (where x is an intermediate result):
*
* - x & 0x3, which is equivalent to x % 3
* - x | 0x8, which is equivalent to x + 8
*
* This results in a nibble between 8 inclusive and 11 exclusive,
* (or 1000 and 1011 in binary), all of which satisfy the variant
* field mask above.
*/
var nibble = (match == 'y') ?
(randomNibble & 0x3 | 0x8) :
randomNibble;
/*
* Ensure the nibble integer is encoded as base 16 (hexadecimal).
*/
return nibble.toString(16);
}
);
};
调整了我自己的UUID / GUID生成器,并在此处添加了一些其他功能。
我正在使用以下Kybos随机数生成器,使其在密码学上更加合理。
下面是我的脚本,其中排除了baagoe.com的Mash和Kybos方法。
//UUID/Guid Generator
// use: UUID.create() or UUID.createSequential()
// convenience: UUID.empty, UUID.tryParse(string)
(function(w){
// From http://baagoe.com/en/RandomMusings/javascript/
// Johannes Baagøe <baagoe@baagoe.com>, 2010
//function Mash() {...};
// From http://baagoe.com/en/RandomMusings/javascript/
//function Kybos() {...};
var rnd = Kybos();
//UUID/GUID Implementation from http://frugalcoder.us/post/2012/01/13/javascript-guid-uuid-generator.aspx
var UUID = {
"empty": "00000000-0000-0000-0000-000000000000"
,"parse": function(input) {
var ret = input.toString().trim().toLowerCase().replace(/^[\s\r\n]+|[\{\}]|[\s\r\n]+$/g, "");
if ((/[a-f0-9]{8}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{12}/).test(ret))
return ret;
else
throw new Error("Unable to parse UUID");
}
,"createSequential": function() {
var ret = new Date().valueOf().toString(16).replace("-","")
for (;ret.length < 12; ret = "0" + ret);
ret = ret.substr(ret.length-12,12); //only least significant part
for (;ret.length < 32;ret += Math.floor(rnd() * 0xffffffff).toString(16));
return [ret.substr(0,8), ret.substr(8,4), "4" + ret.substr(12,3), "89AB"[Math.floor(Math.random()*4)] + ret.substr(16,3), ret.substr(20,12)].join("-");
}
,"create": function() {
var ret = "";
for (;ret.length < 32;ret += Math.floor(rnd() * 0xffffffff).toString(16));
return [ret.substr(0,8), ret.substr(8,4), "4" + ret.substr(12,3), "89AB"[Math.floor(Math.random()*4)] + ret.substr(16,3), ret.substr(20,12)].join("-");
}
,"random": function() {
return rnd();
}
,"tryParse": function(input) {
try {
return UUID.parse(input);
} catch(ex) {
return UUID.empty;
}
}
};
UUID["new"] = UUID.create;
w.UUID = w.Guid = UUID;
}(window || this));
对于那些希望与rfc4122版本4兼容的解决方案并考虑速度的人(对Math.random()的调用很少):
var rand = Math.random;
function UUID() {
var nbr, randStr = "";
do {
randStr += (nbr = rand()).toString(16).substr(3, 6);
} while (randStr.length < 30);
return (
randStr.substr(0, 8) + "-" +
randStr.substr(8, 4) + "-4" +
randStr.substr(12, 3) + "-" +
((nbr*4|0)+8).toString(16) + // [89ab]
randStr.substr(15, 3) + "-" +
randStr.substr(18, 12)
);
}
console.log( UUID() );
以上功能应在速度和随机性之间取得适当的平衡。
ES6样本
const guid=()=> {
const s4=()=> Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
return `${s4() + s4()}-${s4()}-${s4()}-${s4()}-${s4() + s4() + s4()}`;
}
更好的方法:
function(
a,b // placeholders
){
for( // loop :)
b=a=''; // b - result , a - numeric variable
a++<36; //
b+=a*51&52 // if "a" is not 9 or 14 or 19 or 24
? // return a random number or 4
(
a^15 // if "a" is not 15
? // genetate a random number from 0 to 15
8^Math.random()*
(a^20?16:4) // unless "a" is 20, in which case a random number from 8 to 11
:
4 // otherwise 4
).toString(16)
:
'-' // in other cases (if "a" is 9,14,19,24) insert "-"
);
return b
}
最小化:
function(a,b){for(b=a='';a++<36;b+=a*51&52?(a^15?8^Math.random()*(a^20?16:4):4).toString(16):'-');return b}
我知道,这是一个古老的问题。仅出于完整性考虑,如果您的环境是SharePoint,则有一个名为SP.Guid.newGuid
(msdn link)的实用程序功能,它将创建一个新的GUID。此函数在sp.init.js文件中。如果重写此函数(以从其他私有函数中删除一些其他依赖项),则看起来像这样:
var newGuid = function () {
var result = '';
var hexcodes = "0123456789abcdef".split("");
for (var index = 0; index < 32; index++) {
var value = Math.floor(Math.random() * 16);
switch (index) {
case 8:
result += '-';
break;
case 12:
value = 4;
result += '-';
break;
case 16:
value = value & 3 | 8;
result += '-';
break;
case 20:
result += '-';
break;
}
result += hexcodes[value];
}
return result;
};
crypto.getRandomValues(a)
在支持的浏览器(IE11 +,iOS7 +,FF21 +,Chrome,Android Chrome)上使用的简单代码。使用避免了Math.random()
因为可以(在由实际情况例如碰撞20 4000点生成的UUID导致冲突MUXA)。
function uuid() {
function randomDigit() {
if (crypto && crypto.getRandomValues) {
var rands = new Uint8Array(1);
crypto.getRandomValues(rands);
return (rands[0] % 16).toString(16);
} else {
return ((Math.random() * 16) | 0).toString(16);
}
}
var crypto = window.crypto || window.msCrypto;
return 'xxxxxxxx-xxxx-4xxx-8xxx-xxxxxxxxxxxx'.replace(/x/g, randomDigit);
}
笔记:
如果您只需要一个没有特定格式的随机128位字符串,则可以使用:
function uuid() {
return crypto.getRandomValues(new Uint32Array(4)).join('-');
}
这将返回类似的内容2350143528-4164020887-938913176-2513998651
。
Array.from((window.crypto || window.msCrypto).getRandomValues(new Uint32Array(4))).map(n => n.toString(16)).join('-')
只是另一个更具可读性的变体,只有两个突变。
function uuid4()
{
function hex (s, b)
{
return s +
(b >>> 4 ).toString (16) + // high nibble
(b & 0b1111).toString (16); // low nibble
}
let r = crypto.getRandomValues (new Uint8Array (16));
r[6] = r[6] >>> 4 | 0b01000000; // Set type 4: 0100
r[8] = r[8] >>> 3 | 0b10000000; // Set variant: 100
return r.slice ( 0, 4).reduce (hex, '' ) +
r.slice ( 4, 6).reduce (hex, '-') +
r.slice ( 6, 8).reduce (hex, '-') +
r.slice ( 8, 10).reduce (hex, '-') +
r.slice (10, 16).reduce (hex, '-');
}
好的,使用uuid软件包,它支持版本1、3、4和5的UUID:
yarn add uuid
然后:
const uuidv1 = require('uuid/v1');
uuidv1(); // ⇨ '45745c60-7b1a-11e8-9c9c-2d42b21b1a3e'
您也可以使用完全指定的选项进行操作:
const v1options = {
node: [0x01, 0x23, 0x45, 0x67, 0x89, 0xab],
clockseq: 0x1234,
msecs: new Date('2011-11-01').getTime(),
nsecs: 5678
};
uuidv1(v1options); // ⇨ '710b962e-041c-11e1-9234-0123456789ab'
有关更多信息,请在此处访问npm页面
重要的是使用经过良好测试的,由多个贡献者维护的代码,而不要自己动手编写代码。在这里,您可能更喜欢最稳定的代码,而不是在X浏览器中可用的最短巧的版本,但是却没有考虑到Y的特质,这常常导致很难调查错误,而不仅仅是随机出现。对于某些用户。我个人在https://github.com/aurigadl/uuid-js上使用uuid-js,它启用了Bower,因此我可以轻松进行更新。