我正在尝试在JavaScript中创建全局唯一标识符.我不确定所有浏览器上有哪些例程,"随机"和内置随机数生成器的种子等等.
GUID/UUID应至少为32个字符,并应保持在ASCII范围内,以避免传递它们时出现问题.
对于符合RFC4122版本4的解决方案,这种单线程(ish)解决方案是我能想到的最紧凑的解决方案:
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());
根据RFC 4122,UUID(通用唯一标识符),也称为GUID(全局唯一标识符),是具有特定唯一性保证的标识符.
生成它们的最佳方法是遵循上述RFC中的实现指令,使用众多社区审查的开源实现之一,或者对于拥有它的语言,使用内置实现.
这里列出了一些用于处理UUID的开源工具,一些流行的编程语言.
JavaScript的
https://github.com/kelektiv/node-uuid
PHP
https://github.com/ramsey/uuid
走
https://github.com/satori/go.uuid
红宝石
该部分标准库
蟒蛇
在UUID模块中
请注意,只是逐字节或逐字符地随机生成标识符,不会为符合要求的实现提供相同的保证.此外,非常重要的是,使用兼容UUID的系统可能选择不接受随机生成的系统,并且许多开源验证器实际上将检查有效的结构.
UUID必须具有以下格式:
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
凡中号和ñ位置可能只具有一定的价值.此时,M的唯一有效值是1,2,3,4和5,因此随机生成该位置将使大多数结果不可接受.
我真的很喜欢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())
这是一个小小的测试.
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日,我通过在击败了最流行的答案12X通过了,接受的答案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
用随机数据(除了迫使前2位,以10
每RFC规范),以及正则表达式不匹配-
或4
人物,所以他没有来对付他们.非常非常光滑.
知道的第一件事是,函数调用是昂贵的,因为是正则表达式(虽然他只使用1,它有32个回调,一个每场比赛,并且在每个32个回调调用的Math.random()和v.的toString(16)).
性能的第一步是消除RegEx及其回调函数,而是使用简单的循环.这意味着我们必须处理-
和4
字符,而broofa没有.另请注意,我们可以使用字符串数组索引来保持其光滑的字符串模板体系结构:
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())
模块化:http://jcward.com/UUID.js -UUID.generate()
有趣的是,生成16字节的随机数据很容易.整个技巧是以符合RFC的字符串格式表达它,并且它使用16字节的随机数据,一个展开的循环和查找表来完成.
我希望我的逻辑是正确的 - 在这种繁琐的工作中犯错误很容易.但输出对我来说很好看.我希望你通过代码优化享受这种疯狂的旅程!
建议:我的主要目标是展示和教授潜在的优化策略.其他答案涵盖了重要的主题,如冲突和真正的随机数,这对于生成良好的UUID非常重要.
下面是基于一些代码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; }
let uniqueId = Math.random().toString(36).substring(2) + Date.now().toString(36);
document.getElementById("unique").innerHTML =
Math.random().toString(36).substring(2) + (new Date()).getTime().toString(36);
最快的GUID,如格式中的字符串生成器方法XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
.这不会生成符合标准的GUID.
这个实现的1000万次执行只需要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
).
然后我们取这个数字并将其转换为基数为16的字符串(从上面的示例中我们得到
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).这就是为什么我们需要向它添加九个零("0"+"000000000"
或"1"+"000000000"
),然后从长度为八个字符的第二个索引(第三个字符)中删除它.对于其他情况,添加零不会损害结果,因为它无论如何都会切断它.
Math.random().toString(16)+"000000000").substr(2,8)
.
大会:
GUID采用以下格式XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
.
我将GUID分为4个部分,每个部分分为2种类型(或格式):XXXXXXXX
和-XXXX-XXXX
.
现在我正在构建使用这两种类型的GUID来组装带有调用4件的GUID,如下所示:XXXXXXXX
-XXXX-XXXX
-XXXX-XXXX
XXXXXXXX
.
为了区分这两种类型,我在一个对创建器函数中添加了一个flag参数_p8(s)
,该s
参数告诉函数是否添加破折号.
最后我们使用以下链接构建GUID:_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 // /sf/ask/17360801/ 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 // /sf/ask/17360801/#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上如果你想测试它.
这是一个完全不兼容但非常高性能的实现,用于生成类似于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 ) }
来自sagi shkedy的技术博客:
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生成器可以保证唯一键(查看维基百科文章).始终存在碰撞的可能性.GUID只提供足够大的键,以将碰撞的变化减少到几乎为零.
你可以使用node-uuid(https://github.com/kelektiv/node-uuid)
简单,快速生成RFC4122 UUIDS.
特征:
生成RFC4122版本1或版本4 UUID
在node.js和浏览器中运行.
密码学上强大的随机#生成在支持平台上.
占地面积小(想要更小的东西吗?看看这个!)
使用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 /主/ 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();
Web服务很有用.
快速谷歌发现:http://www.hoskinson.net/GuidGenerator/
无法担保此实现,但SOMEONE必须发布一个真正的GUID生成器.
使用这样的Web服务,您可以开发一个使用GUID Web服务的REST Web界面,并通过AJAX在浏览器中将其提供给javascript.
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答案的版本和一些按位运算符从缓冲区中提取半字节大小的窗口.
应该遵循RFC Type 4(随机)模式,因为我上次使用Java的UUID解析不合规的uuids时出现问题.
简单的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());
嗯,这已经有了很多答案,但不幸的是,这一堆中没有"真正的"随机.下面的版本是对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, 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 , 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); }); };
这创建版本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
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('-');
调整与一些额外的我自己的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, 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));
我想了解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); } ); };
对于那些想要符合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()}`; }
这只是一个简单的AJAX调用......
如果有人仍然感兴趣,这是我的解决方案.
在服务器端:
[WebMethod()] public static string GenerateGuid() { return Guid.NewGuid().ToString(); }
在客户端:
var myNewGuid = null; PageMethods.GenerateGuid( function(result, userContext, methodName) { myNewGuid = result; }, function() { alert("WebService call failed."); } );
更好的方法:
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}
这个基于日期,并添加随机后缀以"确保"唯一性.适用于css标识符.它总是返回类似的东西并且很容易破解:
UID-139410573297741
var getUniqueId = function (prefix) { var d = new Date().getTime(); d += (parseInt(Math.random() * 100)).toString(); if (undefined === prefix) { prefix = 'uid-'; } d = prefix + d; return d; };
如果您只需要一个无特殊格式的随机128位字符串,您可以使用:
function uuid() { return crypto.getRandomValues(new Uint32Array(4)).join('-'); }
这将返回类似的东西2350143528-4164020887-938913176-2513998651
.
有一个jQuery插件可以很好地处理Guid的http://plugins.jquery.com/project/GUID_Helper
jQuery.Guid.Value()
返回内部Guid的值.如果未指定guid,则返回一个新值(然后在内部存储值).
jQuery.Guid.New()
返回一个新的Guid并在内部设置它的值.
jQuery.Guid.Empty()
返回空Guid 00000000-0000-0000-0000-000000000000.
jQuery.Guid.IsEmpty()
返回布尔值.如果为空/未定义/空/空,则为真.
jQuery.Guid.IsValid()
返回布尔值.真正有效的guid,如果不是,则为false.
jQuery.Guid.Set()
Retrns Guid.将Guid设置为用户指定的Guid,如果无效,则返回空guid.
我知道,这是一个老问题.为了完整起见,如果您的环境是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); }
笔记:
优化代码可读性而不是速度,所以适合说几百uuid每秒.使用http://jsbin.com/fuwigo/1测量性能,在我的笔记本电脑上每秒在Chromium中生成大约10000 uuid().
仅对"y"使用8,因为这简化了代码可读性(y允许为8,9,A或B).
只是另一个具有两个突变的可读变体.
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, '-'); }
重要的是要使用由超过1个贡献者维护的经过良好测试的代码,而不是为此自己动手.这是你可能希望最喜欢最稳定的代码的地方之一,而不是在X浏览器中工作的最短的聪明版本,但不考虑Y的特性,这通常会导致非常难以调查错误,而不仅仅是随机显示对于一些用户.我个人在https://github.com/aurigadl/uuid-js上使用了uuid-js,它启用了bower,因此我可以轻松获取更新.