如何创建GUID/UUID?

如何在JavaScript中创建GUID(全局唯一标识符)?GUID/UUID应至少为32个字符,并应保持在ASCII范围内,以避免在传递它们时出现问题。

我不确定所有浏览器上都有哪些例程,内置的随机数生成器是如何“随机”和播种的,等等。

2688947 次浏览

UUID(通用唯一标识符),也称为GUID(全局唯一标识符),根据rfc4122,是旨在提供某些唯一性保证的标识符。

虽然可以在几行JavaScript代码中实现符合RFC的UUID(例如,请参阅下面的@broofa的回答),但有几个常见的陷阱:

  • 无效的id格式(UUID必须为“xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx”形式,其中x是[0-9之一,a-f]M是[1-5]之一,N是[8,9, a或b]
  • 使用低质量的随机性源(例如Math.random

因此,鼓励为生产环境编写代码的开发人员使用严格的、维护良好的实现,例如uuid模块。

sagi shkady的技术博客

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只是提供了足够大的键宇宙,将碰撞的变化减少到几乎为零。

下面是一些基于rfc4122的代码,第4.4节(从真随机数或伪随机数创建UUID的算法)。

function createUUID() {// http://www.ietf.org/rfc/rfc4122.txtvar 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 0010s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);  // bits 6-7 of the clock_seq_hi_and_reserved to 01s[8] = s[13] = s[18] = s[23] = "-";
var uuid = s.join("");return uuid;}

这将创建一个版本4UUID(由伪随机数创建):

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-25ba9613a75c97d19478-3ab2-4aa1-b8cc-a1c3540f54aa2eed04c9-2692-456d-a0fd-51012f947136

[编辑2021-10-16以反映生产符合RFC4122的UUID的最新最佳实践]

这里的大多数读者都希望使用#0模块。它经过了良好的测试和支持。

#0函数是Node.js越来越多的浏览器中支持的新兴标准。然而,由于新的浏览器API仅限于安全上下文,此方法仅适用于本地(localhost127.0.0.1)或通过HTTPS提供服务的页面。如果您有兴趣看到crypto.randomUUID()取消此限制,您可以遵循这个github问题

如果这两种方法都不适用于您,则有以下方法(基于此问题的原始答案):

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());

注意:原因最好在这里解释强烈反对使用依赖于#0的任何UUID生成器(包括本答案以前版本中的片段)。太长别读:基于Math.random()的解决方案不能提供良好的唯一性保证。

  // 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 valuesvar 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('-');

这是一个日期为2011年10月9日的解决方案,来自用户jedhttps://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 15a ^ // 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 withb // random hex digits)}
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解析不兼容的UUID时使用了问题

以下是最高投票答案的组合,以及Chrome碰撞的解决方法:

generateGUID = (typeof(window.crypto) != 'undefined' &&typeof(window.crypto.getRandomValues) != 'undefined') ?function() {// If we have a cryptographically secure PRNG, use that// https://stackoverflow.com/questions/6906916/collisions-when-generating-uuids-in-javascriptvar 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// https://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523return '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

我真的很喜欢Broofa的回答有多干净,但不幸的是#0的不良实现留下了碰撞的机会。

这是一个类似的RFC4122版本4兼容解决方案,它通过将前13个十六进制数字抵消时间戳的十六进制部分来解决这个问题,并且一旦将偏移量耗尽,则自页面加载以来的微秒的十六进制部分。这样,即使Math.random在同一个种子上,两个客户端也必须生成自页面加载以来完全相同的微秒数(如果支持高性能时间)的UUID,并且在完全相同的毫秒(或10,000多年后)获得相同的UUID:

function generateUUID() { // Public Domain/MITvar d = new Date().getTime();//Timestampvar d2 = ((typeof performance !== 'undefined') && performance.now && (performance.now()*1000)) || 0;//Time in microseconds since page-load or 0 if unsupportedreturn 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {var r = Math.random() * 16;//random number between 0 and 16if(d > 0){//Use timestamp until depletedr = (d + r)%16 | 0;d = Math.floor(d/16);} else {//Use microseconds since page-load if supportedr = (d2 + r)%16 | 0;d2 = Math.floor(d2/16);}return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);});}
var onClick = function(){document.getElementById('uuid').textContent = generateUUID();}onClick();
#uuid { font-family: monospace; font-size: 1.5em; }
<p id="uuid"></p><button id="generateUUID" onclick="onClick();">Generate UUID</button>

这里有一个小提琴来测试。


ES6的现代化代码段

const generateUUID = () => {letd = new Date().getTime(),d2 = (performance && performance.now && (performance.now() * 1000)) || 0;return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {let r = Math.random() * 16;if (d > 0) {r = (d + r) % 16 | 0;d = Math.floor(d / 16);} else {r = (d2 + r) % 16 | 0;d2 = Math.floor(d2 / 16);}return (c == 'x' ? r : (r & 0x7 | 0x8)).toString(16);});};
const onClick = (e) => document.getElementById('uuid').textContent = generateUUID();
document.getElementById('generateUUID').addEventListener('click', onClick);
onClick();
#uuid { font-family: monospace; font-size: 1.5em; }
<p id="uuid"></p><button id="generateUUID">Generate UUID</button>

我调整了自己的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.aspxvar 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;elsethrow 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 partfor (;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));

更好的方法:

function(a, b               // Placeholders){for(               // Loop :)b = a = '';    // b - result , a - numeric variablea++ < 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,?              // generate a random number from 0 to 158^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}

GitHub上的JavaScript项目-https://github.com/LiosK/UUID.js

UUID.js符合RFC的JavaScriptUUID生成器。

参见RFC 4122http://www.ietf.org/rfc/rfc4122.txt

功能生成符合RFC 4122的UUID。

版本4 UUID(来自随机数的UUID)和版本1 UUID(基于时间的UUID)可用。

UUID对象允许对UUID进行多种访问,包括访问UUID

JavaScript的低时间戳分辨率由随机补偿数。

这是一个完全不兼容但非常高性能的实现,用于生成类似ASCII安全GUID的唯一标识符。

function generateQuickGuid() {return Math.random().toString(36).substring(2, 15) +Math.random().toString(36).substring(2, 15);}

生成26个[a-z0-9]字符,生成比RFC兼容GUID更短且更独特的UID。如果需要易读性,可以简单地添加破折号。

以下是这个函数的用法示例和计时,以及这个问题的其他几个答案。计时是在Chromem25下执行的,每次迭代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');

对于那些想要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() );

上面的函数应该在速度和随机性之间有一个不错的平衡。

简单的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);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 GUIDconsole.log('1. New Guid:   ' + Guid.newGuid());
// DEMO: Show empty GUIDconsole.log('2. Empty Guid: ' + Guid.empty);

用法:

Guid.newGuid()

"c 6 c 2 d 12 f-d76b-5739-e 551-07 e 6 de 5 b 0807"

Guid.empty

"00000000-0000-0000-0000-000000000000"

这是格式XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX中最快的类似GUID的字符串生成器方法。它不生成符合标准的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)。
  • 然后我们取这个数字并转换它是一个以16为基数的字符串(从上面的例子中,我们将得到0.6fb7687f)。Math.random().toString(16).
  • 然后我们切断0.前缀(0.6fb7687f=>6fb7687f)得到一个十六进制为8的字符串字符长。(Math.random().toString(16).substr(2,8).
  • 有时Math.random()函数会返回更短的数字(例如0.4363),由于末尾有零(从上面的例子中,实际上数字是0.4363000000000000)。这就是为什么我要附加到这个字符串"000000000"(一个有九个零的字符串),然后用substr()函数将其切断,使其精确为九个字符(向右填充零)。
  • 正好加9个零是因为更糟糕的情况,也就是Math.random()函数正好返回0或1(每个0的概率为1/10^16)。这就是为什么我们需要给它加9个零("0"+"000000000""1"+"000000000"),然后从长度为8个字符的第二个索引(第三个字符)中切断它。对于其余的情况,添加零不会损害结果,因为它无论如何都会切断它。Math.random().toString(16)+"000000000").substr(2,8).

大会:

  • GUID格式如下XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
  • 我将GUID分为四个部分,每个部分分为两种类型(或格式):XXXXXXXX-XXXX-XXXX
  • 现在我正在使用这两种类型构建GUID,以使用调用四个部分组装GUID,如下所示:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
  • 为了区分这两种类型,我向一对创建者函数_p8(s)添加了一个标志参数,s参数告诉函数是否添加破折号。
  • 最终,我们使用以下链接构建GUID:_p8() + _p8(true) + _p8(true) + _p8(),并返回它。

链接到我博客上的这篇文章

享受!:-)

如果您的环境是SharePoint,则有一个名为SP.Guid.newGuidmsdn链接)的实用函数,它会创建一个新的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;};

重要的是使用由多个贡献者维护的经过良好测试的代码,而不是为此鞭打自己的东西。

这是你可能希望选择最稳定的代码而不是在X浏览器中工作的最短的聪明版本的地方之一,但不考虑Y的特性,这通常会导致非常难以调查的错误,而不是对某些用户随机显示。就我个人而言,我在https://github.com/aurigadl/uuid-js使用uuid-js,它启用了鲍尔,所以我可以轻松地进行更新。

Broofa的回答非常光滑,确实-令人印象深刻的聪明,真的……符合RFC4122,有点可读,紧凑。太棒了!

但是,如果您正在查看正则表达式,许多replace()回调,toString()Math.random()函数调用(他只使用了结果的四位并浪费了其余部分),您可能会开始怀疑性能。事实上,joelpt甚至决定放弃RFC以获得generateQuickGUID的通用GUID速度。

但是,我们能得到速度和RFC合规性吗?我说,是的!我们能保持易读性吗?嗯……不太可能,但如果你遵循的话很容易。

但首先,我的结果,与broofa,guid(接受的答案)和不符合rfc的generateQuickGuid相比:

                  Desktop   Androidbroofa: 1617ms   12869mse1:  636ms    5778mse2:  606ms    4754mse3:  364ms    3003mse4:  329ms    2015mse5:  147ms    1156mse6:  146ms    1035mse7:  105ms     726msguid:  962ms   10762msgenerateQuickGuid:  292ms    2961ms- Note: 500k iterations, results will vary by browser/CPU.

因此,在我的第六次优化迭代中,我将最受欢迎的答案击败了12次,将接受的答案击败了9次,将快速不兼容的答案击败了2-3次。我仍然符合RFC 4122标准。

怎么感兴趣?我已经把完整的源代码放在http://jsfiddle.net/jcward/7hyaC/3/https://jsben.ch/xczxS上了

为了解释,让我们从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替换为随机数据(除了根据RFC规范将前两位强制为10),并且正则表达式与-4字符不匹配,因此他不必处理它们。非常非常光滑。

首先要知道的是函数调用是昂贵的,正则表达式也是如此(尽管他只使用1,但它有32个回调,每个匹配一个,并且在32个回调中的每一个都调用Math.random()和v.toString(16))。

提高性能的第一步是消除RegEx及其回调函数,转而使用简单的循环。这意味着我们必须处理-4字符,而broofa没有。此外,请注意,我们可以使用String Array索引来保持他光滑的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,并且使用了一个这时候循环(而不是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很重要。

这个基于日期,并添加了一个随机后缀来“确保”唯一性。

它适用于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;};

我正在使用下面的函数:

function NewGuid(){var sGuid = "";for (var i=0; i<32; i++){sGuid += Math.floor(Math.random()*0xF).toString(0xF);}return sGuid;}

对于我的用例,我需要生成保证全局唯一的id;无一例外。我与这个问题斗争了一段时间,并提出了一个名为TUID(真正唯一的ID)的解决方案。它生成一个id,其中前32个字符是系统生成的,其余数字代表自纪元以来的毫秒。在我需要在客户端JavaScript代码中生成id的情况下,它运行良好。

下面的版本是对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 itvar 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 numbersvar 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 lookupsrval = rval & mask;
if (rval >= range) {// Integer out of acceptable rangereturn random(min, max);}// Return an integer that falls within the rangereturn 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>, 2010function 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>, 2010var 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^-32s0 = 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);});};

我想理解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-”作为前缀。

下面的函数将生成一个类型为u u id-14 d 93 eb 1 b 9 b 4533 e 6的随机字符串。不需要生成32个字符的随机字符串。在这种情况下,16个字符的随机字符串足以提供JavaScript中唯一的UUID。

var createUUID = function() {return "uuid-" + ((new Date).getTime().toString(16) + Math.floor(1E7*Math.random()).toString(16));}

以下是在支持的浏览器上使用crypto.getRandomValues(a)的简单代码(Internet Explorer 11+,iOS7+,Firefox 21+,Chrome和AndroidChrome)。

它避免使用Math.random(),因为这可能会导致碰撞(例如,Muxa在实际情况下为4000个生成的UUID发生20次碰撞)。

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。它在我的笔记本电脑上Chromium每秒生成大约10000个uuid(),使用http://jsbin.com/fuwigo/1来衡量性能。
  • 它只使用8表示“y”,因为这简化了代码的易读性(y可以是89一个B)。

另一种方法做同样的事情:

function guid() {var chars = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"];var str = "";for(var i=0; i<36; i++) {var str = str + ((i == 8 || i == 13 || i == 18 || i == 23) ? "-" : chars[Math.floor(Math.random()*chars.length)]);};return str;}

您可以使用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/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 namespaceconst MY_NAMESPACE = '(previously generated unique uuid string)';uuidV5('hello', MY_NAMESPACE); // -> '90123e1c-7512-523e-bb28-76fab9f2f73d'

ECMAScript 2015(ES6):

import uuid from 'uuid/v4';const id = uuid();

您可以使用npm包guid、GUID生成器和验证器。

示例:

Guid.raw();// -> '6fdf6ffc-ed77-94fa-407e-a7b86ed9e59d'

备注:此包已被弃用。请改用uuid

示例:

const uuidv4 = require('uuid/v4');uuidv4(); // ⇨ '10ba038e-48da-487b-96e8-8d3b99b6d18a'

如果有人在谷歌上寻找一个小型实用程序库,短消息类型满足了这个问题的所有要求。它允许指定允许的字符和长度,并保证非顺序、非重复字符串。

为了使这更像一个真正的答案,该库的核心使用以下逻辑来生成其短id:

function encode(lookup, number) {var loopCounter = 0;var done;
var str = '';
while (!done) {str = str + lookup( ( (number >> (4 * loopCounter)) & 0x0f ) | randomByte() );done = number < (Math.pow(16, loopCounter + 1 ) );loopCounter++;}return str;}
/* Generates the short id */function generate() {
var str = '';
var seconds = Math.floor((Date.now() - REDUCE_TIME) * 0.001);
if (seconds === previousSeconds) {counter++;} else {counter = 0;previousSeconds = seconds;}
str = str + encode(alphabet.lookup, version);str = str + encode(alphabet.lookup, clusterWorkerId);if (counter > 0) {str = str + encode(alphabet.lookup, counter);}str = str + encode(alphabet.lookup, seconds);
return str;}

我没有编辑它来反映只有这种方法的最基本部分,所以上面的代码包含了一些来自库的额外逻辑。如果你对它所做的一切都很好奇,请查看源代码:https://github.com/dylang/shortid/tree/master/lib

这是一个工作示例。它生成一个32位的独特UUID。

function generateUUID() {var d = new Date();var k = d.getTime();var str = k.toString(16).slice(1)var UUID = 'xxxx-xxxx-4xxx-yxxx-xzx'.replace(/[xy]/g, function (c){var r = Math.random() * 16 | 0;v = c == 'x' ? r : (r & 3 | 8);return v.toString(16);});
var newString = UUID.replace(/[z]/, str)return newString;}
var x = generateUUID()console.log(x, x.length)

我发现这个脚本对于在JavaScript中创建GUID很有用

var myGuid = GUID();

这可能对某人有用…

var d = new Date().valueOf();var n = d.toString();var result = '';var length = 32;var p = 0;var chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
for (var i = length; i > 0; --i){result += ((i & 1) && n.charAt(p) ? '<b>' + n.charAt(p) + '</b>' : chars[Math.floor(Math.random() * chars.length)]);if(i & 1) p++;};

function randomHex(length) {var random_string = '';if(!length){length = 1;}for(var i=0; i<length; i+=1){random_string += Math.floor(Math.random() * 15).toString(16);}return random_string;}
function guid() {return randomHex(8);}

在这里您可以找到生成UUID的一个很小的功能

其中一个最终版本是:

function b(a                  // Placeholder){var cryptoObj = window.crypto || window.msCrypto; // For Internet Explorer 11return a           // If the placeholder was passed, return? (              // a random number from 0 to 15a ^            // unless b is 8,cryptoObj.getRandomValues(new Uint8Array(1))[0]  // 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 withb            // random hex digits)}

如果您只需要一个没有特定格式的随机128位字符串,您可以使用:

function uuid() {return crypto.getRandomValues(new Uint32Array(4)).join('-');}

这将返回类似于2350143528-4164020887-938913176-2513998651的内容。

用途:

let uniqueId = Date.now().toString(36) + Math.random().toString(36).substring(2);

document.getElementById("unique").innerHTML =Math.random().toString(36).substring(2) + (new Date()).getTime().toString(36);
<div id="unique"></div>

如果ID的生成间隔超过1毫秒,则它们是100%唯一的。

如果以较短的间隔生成两个ID,并假设随机方法是真正随机的,这将生成99.99999999999999%可能是全局唯一的ID(10^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('-');

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('-');document.getElementById("unique").innerHTML = guid;
<div id="unique"></div>

上面的代码遵循意图,但不是RFC的字母。在其他差异中,它短了几个随机数字。(如果需要,可以添加更多的随机数字)好处是这真的很快:)在此处测试您的GUID的有效性

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 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: 0100r[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, '-');}

对于那些在Windows上使用JavaScript的人(例如,Windows脚本主机(WSH)、CScriptHTA)。可以使用ActiveX。具体来说,Scriptlet.Typelib对象:

WScript.Echo((new ActiveXObject("Scriptlet.TypeLib")).Guid)

请注意,此答案仅适用于我列出的技术。它不适用于任何浏览器,甚至MicrosoftEdge!因此,您的里程将因此答案而异。

基于Broofa的工作,我通过将时间戳添加到math.random()来添加更多随机性:

function uuidv4() {return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {var r = parseFloat('0.' + Math.random().toString().replace('0.', '') + new Date().getTime()) * 16 | 0,v = c == 'x' ? r : (r & 0x3 | 0x8);return v.toString(16);});}

以下代码不兼容v4,但可以轻松更改为兼容v4。这只是扩展Uint8Array类型并使用crypto.get随机值()生成UUID字节值的示例。

class uuid extends Uint8Array {constructor() {super(16)/* Not v4, just some random bytes */window.crypto.getRandomValues(this)}toString() {let id = new String()for (let i = 0; i < this.length; i++) {/* Convert uint8 to hex string */let hex = this[i].toString(16).toUpperCase()
/* Add zero padding */while (hex.length < 2) {hex = String(0).concat(hex)}id += hex
/* Add dashes */if (i == 4 || i == 6 || i == 8 || i == 10 || i == 16) {id += '-'}}return id}}

我找不到任何使用单个16个八位字节TypedArrayDataView的答案,所以我认为以下用于生成每个的rfc版本4UUID的解决方案将在这里独立存在:

const uuid4 = () => {const ho = (n, p) => n.toString(16).padStart(p, 0); /// Return the hexadecimal text representation of number `n`, padded with zeroes to be of length `p`const data = crypto.getRandomValues(new Uint8Array(16)); /// Fill the buffer with random datadata[6] = (data[6] & 0xf) | 0x40; /// Patch the 6th byte to reflect a version 4 UUIDdata[8] = (data[8] & 0x3f) | 0x80; /// Patch the 8th byte to reflect a variant 1 UUID (version 4 UUIDs are)const view = new DataView(data.buffer); /// Create a view backed by a 16-byte bufferreturn `${ho(view.getUint32(0), 8)}-${ho(view.getUint16(4), 4)}-${ho(view.getUint16(6), 4)}-${ho(view.getUint16(8), 4)}-${ho(view.getUint32(10), 8)}${ho(view.getUint16(14), 4)}`; /// Compile the canonical textual form from the array data};

我更喜欢它,因为它只依赖于标准ECMAScript平台可用的函数,在可能的情况下——这是除了一个过程之外的所有过程。

在撰写本文时,getRandomValues不是为Node.js.中的crypto对象实现的,但是它具有等效的randomBytes函数,可以使用它。

好的,使用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页面这里

我们可以使用替换和crypto.get随机值来获得这样的输出:

xxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxxxxxx

在此输入图片描述

如果我们正在寻找一个opti解决方案,我们必须将crypto.getRandomValues(new Uint8Array(1))[0]替换为数组(32)。

const uuidv4 = () =>([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16));
console.log(uuidv4());

要获取此代码:

function uuidv4() {let bytes = window.crypto.getRandomValues(new Uint8Array(32));const randomBytes = () => (bytes = bytes.slice(1)) && bytes[0];
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>(c ^ randomBytes() & 15 >> c / 4).toString(16));}

for (var i = 0; i < 10; i++)console.log(uuidv4());

碰撞:

我们可以像谷歌分析一样添加时间戳:uuidv4() + "." + (+new Date())

UUID目前有一个添加到标准库的建议,可以在这里得到支持ECMAScript提案:JavaScript标准库UUID

该提案包括UUID如下:

// We're not yet certain as to how the API will be accessed (whether it's in the global, or a// future built-in module), and this will be part of the investigative process as we continue// working on the proposal.uuid(); // "52e6953d-edbe-4953-be2e-65ed3836b2f0"

此实现遵循与此处找到的V4随机UUID生成相同的布局:https://www.npmjs.com/package/uuid

const uuidv4 = require('uuid/v4');uuidv4(); // ⇨ '1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed'

我认为值得注意的是,了解在标准库中正式实现可以节省多少带宽。

12 kBuuid模块每月从npm>62,000,000次(2019年6月)下载;使其在标准库中可用最终会在全球范围内节省TB的带宽。如果我们继续使用标准库满足用户需求,例如uuid,带宽节省会增加。

Broofa的更新从2017-06-28的TypeScript版本,基于crypto API:

function genUUID() {// Reference: https://stackoverflow.com/a/2117523/709884return ("10000000-1000-4000-8000-100000000000").replace(/[018]/g, s => {const c = Number.parseInt(s, 10)return (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)})}

理由:

  • number[]number之间使用+是无效的
  • stringnumber的转换必须是显式的

我已经构建了这里提到的所有内容,以产生两倍的速度,可移植的所有环境,包括节点,并从Math.random()升级到加密强度随机性。你可能不认为UUID需要加密强度,但这意味着碰撞的可能性更小,这是UUID的全部意义。

function random() {constfourBytesOn = 0xffffffff, // 4 bytes, all 32 bits on: 4294967295c = typeof crypto === "object"? crypto // Node.js or most browsers: typeof msCrypto === "object" // Stinky non-standard Internet Explorer? msCrypto // eslint-disable-line no-undef: null; // What old or bad environment are we running in?return c? c.randomBytes? parseInt(c.randomBytes(4).toString("hex"), 16) / (fourBytesOn + 1) - Number.EPSILON // Node.js: c.getRandomValues(new Uint32Array(1))[0] / (fourBytesOn + 1) - Number.EPSILON // Browsers: Math.random();}
function uuidV4() { // eslint-disable-line complexity// If possible, generate a single random value, 128 bits (16 bytes)// in length. In an environment where that is not possible, generate// and make use of four 32-bit (4-byte) random values.// Use crypto-grade randomness when available, else Math.random()constc = typeof crypto === "object"? crypto // Node.js or most browsers: typeof msCrypto === "object" // Stinky non-standard Internet Explorer? msCrypto // eslint-disable-line no-undef: null; // What old or bad environment are we running in?letbyteArray = c? c.randomBytes? c.randomBytes(16) // Node.js: c.getRandomValues(new Uint8Array(16)) // Browsers: null,uuid = [ ];
/* eslint-disable no-bitwise */if ( ! byteArray) { // No support for generating 16 random bytes// in one shot -- this will be slowerconstint = [random() * 0xffffffff | 0,random() * 0xffffffff | 0,random() * 0xffffffff | 0,random() * 0xffffffff | 0];byteArray = [ ];for (let i = 0; i < 256; i++) {byteArray[i] = int[i < 4 ? 0 : i < 8 ? 1 : i < 12 ? 2 : 3] >> i % 4 * 8 & 0xff;}}byteArray[6] = byteArray[6] & 0x0f | 0x40; // Always 4, per RFC, indicating the versionbyteArray[8] = byteArray[8] & 0x3f | 0x80; // Constrained to [89ab], per RFC for version 4for (let i = 0; i < 16; ++i) {uuid[i] = (byteArray[i] < 16 ? "0" : "") + byteArray[i].toString(16);}uuid =uuid[ 0] + uuid[ 1] + uuid[ 2] + uuid[ 3] + "-" +uuid[ 4] + uuid[ 5]                       + "-" +uuid[ 6] + uuid[ 7]                       + "-" +uuid[ 8] + uuid[ 9]                       + "-" +uuid[10] + uuid[11] + uuid[12] + uuid[13] + uuid[14] + uuid[15];return uuid;/* eslint-enable no-bitwise */}

在任何情况下都不要使用Math.random,因为它生成随机数的非加密源。

下面的解决方案使用crypto.get随机值

function uuidv4() {return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {// tslint:disable-next-line: no-bitwiseconst r =(window.crypto.getRandomValues(new Uint32Array(1))[0] *Math.pow(2, -32) * 16) |0;// tslint:disable-next-line: no-bitwiseconst v = c === "x" ? r : (r & 0x3) | 0x8;return v.toString(16);});}

此链接帮助你理解Fortify扫描仪抛出的不安全随机性

本机URL.createObjectURL正在生成UUID。您可以利用这一点。

function uuid() {const url = URL.createObjectURL(new Blob())const [id] = url.toString().split('/').reverse()URL.revokeObjectURL(url)return id}

这也适用于Node.js,如果你用let buffer = crypto.randomBytes(16)替换let buffer = new Uint8Array(); crypto.getRandomValues

它应该在性能上击败大多数正则表达式解决方案。

const hex = '0123456789ABCDEF'
let generateToken = function() {let buffer = new Uint8Array(16)
crypto.getRandomValues(buffer)
buffer[6] = 0x40 | (buffer[6] & 0xF)buffer[8] = 0x80 | (buffer[8] & 0xF)
let segments = []
for (let i = 0; i < 16; ++i) {segments.push(hex[(buffer[i] >> 4 & 0xF)])segments.push(hex[(buffer[i] >> 0 & 0xF)])
if (i == 3 || i == 5 || i == 7 || i == 9) {segments.push('-')}}
return segments.join('')}
for (let i = 0; i < 100; ++i) {console.log(generateToken())}

性能图表(每个人都喜欢它们):jsBench

var guid = createMyGuid();
function createMyGuid(){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);});}

内置时间戳的UUID(发射器/解析器)

这是我生成有效UUIDv4的简单方法,具有非常强的唯一性和快速的运行时。

基本思想并不新鲜,但方法不同。我使用date.now()的毫秒时间戳(在Node.js库中,我稍后会指出,我使用process.hrtime.bigint()的纳秒时间戳),然后在时间戳字符串的末尾添加一个随机的5位数(10000-90000)。

合并字符串后,我只是用数字和一对特殊字符形成一个有效的UUID,这样我的UUID只由数字和一些非数字字符组成。请在下面查看:

/** uuid-timestamp (emitter)* UUID v4 based on timestamp** Created by tarkh* tarkh.com (C) 2020*/const uuidEmit = () => {// Get now timeconst n = Date.now();// Generate randomconst r = Math.random();// Stringify now time and generate additional random numberconst s = String(n) + String(~~(r*9e4)+1e4);// Form UUID and return itreturn `${s.slice(0,8)}-${s.slice(8,12)}-4${s.slice(12,15)}-${[8,9,'a','b'][~~(r*3)]}${s.slice(15,18)}-${s.slice(s.length-12)}`;};
// Generate 5 UUIDsconsole.log(`${uuidEmit()}${uuidEmit()}${uuidEmit()}${uuidEmit()}${uuidEmit()}`);

查看结果,你显然可以看到UUID的第一部分是相同的,然后是随机性。这是因为我线性地将时间戳插入UUID。代码将每毫秒(库中Node.js纳秒)产生一个新的UUID+在末尾添加一个随机的5位数,所以我们最终得到的非常近似的碰撞概率约为11000万。如果我们使用Node.js库,我们的非常近似的碰撞概率为1100亿每秒。

时间戳内置于UUID

由于我们线性地将时间戳插入UUID,因此我们获得了一个功能(好或坏-取决于任务)-能够轻松地从UUID中提取此时间戳。这样我们就可以了解UUID何时发布:

/** uuid-timestamp (parser)* UUID v4 based on timestamp** Created by tarkh* tarkh.com (C) 2020*/const uuidParse = (uuid) => {// Get current timestamp string lengthlet tl = String(Date.now()).length;// Strip out timestamp from UUIDlet ts = '';let i = -1;while(tl--) {i++;if(i===8||i===13||i===14||i===18||i===19||i===23) {tl++;continue;}ts += uuid[i];}return Number(ts);};
// Get the timestamp when UUID was emittedconst time = uuidParse('15970688-7109-4530-8114-887109530114');
// Covert timestamp to date and print itconsole.log(new Date(time).toUTCString());

Node.js

上面代码的NPM版本可作为Node.js模块使用。此版本在生成唯一值方面更强大,因为它使用系统时间和process.hrtime.bigint()差异的组合中的nanoseconds而不是毫秒时间戳。

基准

在我的文章的最后,我想根据这个主题的一些答案做一些性能测试。当然,我的决定不是最快的,但它肯定会占据最高的位置。

在这里检查jsBench

实际上,GUID或UUID在非Microsoft圈子中称为,只是一个128位加密随机数,UUID版本号(1-5)位于固定位置字节。

所以当你只是生成一堆0到65535之间的随机数并对它们进行十六进制编码时,就像这样:

function guid(){function s4(){return Math.floor(Math.random() * 65536).toString(16).padStart(4, '0')} // End Function s4
return s4() + s4() + '-' + s4() + '-' + "4" + s4().substr(1) + '-' + s4() + '-' + s4() + s4() + s4();} // End Function guid

您获得了一个有效的GUID,但由于随机实现,它在加密上不安全。

要生成加密安全的GUID,您需要使用window.crypto(或window.msCrypto for Internet Explorer)。

它是这样的:

function cryptGuid(){var array = new Uint16Array(8);(window.crypto || window.msCrypto).getRandomValues(array);var dataView = new DataView(array.buffer);
var parts = [];
for(var i = 0; i < array.length; ++i){// 0&1,2,3,4,5-7 dataView.getUint16(0-7)if(i>1 && i<6) parts.push("-");parts.push(dataView.getUint16(i).toString(16).padStart(4, '0'));}
parts[5] = "4" + parts[5].substr(1);// console.log(parts);return parts.join('').toUpperCase();// .toLowerCase();}
cryptGuid();

另外,您必须决定将数字返回为小写或大写字符串。某些软件需要小写字符(例如,报告服务),而其他软件则生成大写字符(SQL服务器)。

这里有许多正确答案,但遗憾的是,包含的代码示例非常神秘且难以理解。这就是我创建版本4(随机)UUID的方式。

请注意,以下代码使用二进制文字来提高易读性,因此需要ECMAScript 6。

Node.js版本

function uuid4() {let array = new Uint8Array(16)crypto.randomFillSync(array)
// Manipulate the 9th bytearray[8] &= 0b00111111 // Clear the first two bitsarray[8] |= 0b10000000 // Set the first two bits to 10
// Manipulate the 7th bytearray[6] &= 0b00001111 // Clear the first four bitsarray[6] |= 0b01000000 // Set the first four bits to 0100
const pattern = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"let idx = 0
return pattern.replace(/XX/g,() => array[idx++].toString(16).padStart(2, "0"), // padStart ensures a leading zero, if needed)}

浏览器版本

只有第二行是不同的。

function uuid4() {let array = new Uint8Array(16)crypto.getRandomValues(array)
// Manipulate the 9th bytearray[8] &= 0b00111111 // Clear the first two bitsarray[8] |= 0b10000000 // Set the first two bits to 10
// Manipulate the 7th bytearray[6] &= 0b00001111 // Clear the first four bitsarray[6] |= 0b01000000 // Set the first four bits to 0100
const pattern = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"let idx = 0
return pattern.replace(/XX/g,() => array[idx++].toString(16).padStart(2, "0"), // padStart ensures a leading zero, if needed)}

测试

最后是相应的测试(茉莉)。

describe(".uuid4()", function() {it("returns a UUIDv4 string", function() {const uuidPattern = "XXXXXXXX-XXXX-4XXX-YXXX-XXXXXXXXXXXX"const uuidPatternRx = new RegExp(uuidPattern.replaceAll("X", "[0-9a-f]").replaceAll("Y", "[89ab]"))
for (let attempt = 0; attempt < 1000; attempt++) {let retval = uuid4()expect(retval.length).toEqual(36)expect(retval).toMatch(uuidPatternRx)}})})

UUIDv4解释

UUID版本4的一个很好的解释在这里:生成符合RFC 4122的UUID

最后说明

第三方软件包也有很多,但是我不推荐,只要你有基本的需求就行。真的,没有什么好赢的,也没什么好输的。开发者可能会追求最小的性能,“修复”一些不应该修复的东西,在安全方面,这是一个有风险的想法。同样,他们可能会引入其他bug或不兼容的地方。仔细的更新需要时间。

以下uuid实施提供了使用BigInt不同 ES6 2020解决方案,并专注于“uuid设计模式的用例意图”;特别是用于索引数据库primaryKey场景,其中统一时间顺序和排序规则很有价值。

所以,注意到这篇文章有30多个答案,这里是…

本文链接:

  1. A"太长别读"code部分 w/自足es6class Xuid
  2. a用例动机讨论部分关于es6class Xuid提供代码

太长别读class Xuid解决方案对于使用单调时钟的通用v4#1

下面的代码是从我编写和拥有的SmallScriptEdgeS Web客户端库中提取的,并在此处提供,免费MIT许可。一个GitHub版本将是可用的一旦EdgeS Web客户端工具集被释放。

用法示例:

价钱:console.log(Xuid.v4New)
#{1eb4a659-8bdc-4ce0-c002-b1d505d38ea8}

class Xuid {//@ edges.sm.st, ess.dev: MIT license Smallscript/David Simmons 2020//! Can't use `static const field = const` xbrowser (thus, const's duped)static get v4New() {const ns7Now = this.ns7Now, xnode48 = this.xnode48; let clock_seq13// monotonic `clock_seq` guarantee (13-bits/time-quantum)if(ns7Now <= this.ns7Now_prevSeq && this.ns7Now_prevSeq)clock_seq13 = ((this.ns7Now_prevSeq += 1n) - ns7Now) & 0b1_1111_1111_1111nelseclock_seq13 = 0n, this.ns7Now_prevSeq = ns7Nowconst time60 = ((ns7Now << 4n) & 0xFFFF_FFFF_FFFF_0000n) |(ns7Now & 0x0000_0000_0000_0FFFn),v4 = 0x1_00000000_0000_0000_0000_000000000000n |(time60 << 64n) | (0x00000000_0000_4000_0000_000000000000n) | // M: V4(0b110n << 61n) | (clock_seq13 << 48n) | // N: Variant-2 time-seq collationxnode48, s = v4.toString(16)//.substr(1)return `{${s.substr(1,8)}-${s.substr(9,4)}-${s.substr(13,4)}-${s.substr(17,4)}-${s.substr(21,12)}}`}static get xnode48()/*:<BigInt#48>*/{if(this.xnode48_) return this.xnode48_let clockSeqNode; if(typeof URL !== 'undefined' && URL.createObjectURL) {const url = URL.createObjectURL(new Blob())const id = (url.toString().split('/').reverse()[0]).split('-')URL.revokeObjectURL(url)clockSeqNode = BigInt('0x'+id[3]+id[4])}else {const a4 = this.a4; this.getRandomValues(this.a4);clockSeqNode = (BigInt(a4[2]) << 32n) | BigInt(a4[3])}// simulate the 48-bit node-id and 13-bit clock-seq// to combine with 3-bit uuid-variantreturn this.xnode48_ = clockSeqNode & 0xFFFF_FFFF_FFFFn;}static get jdNow()/*:<double#ns7>*/{// return 2440587.5+Date.now()/864e5 // <- Date-quantum-ms form (7ns form below)return this.jdFromNs7(this.ns7Now)}static get ns7Now()/*:<BigInt#60>*/{if(typeof performance !== 'undefined' && performance.now)Reflect.defineProperty(this, 'ns7Now',Reflect.getOwnPropertyDescriptor(this,'ns7Now_performance'))elseReflect.defineProperty(this, 'ns7Now',Reflect.getOwnPropertyDescriptor(this, 'ns7Now_Date'))return this.ns7Now}static get ns7Now_Date()/*:<BigInt#60>*/{// const epoch1582Ns7_bias = 0x1b2_1dd2_1381_4000  // V1 1582 Oct 15// const epoch1601Ns7_bias = 0x19d_b1de_d53e_8000n // FILETIME baseconst epoch1970Ns7 = BigInt(Date.now() * 1000_0.0)return epoch1970Ns7 + 0x1b2_1dd2_1381_4000n}static get ns7Now_performance()/*:<BigInt#60>*/{const epochPgNs7 = BigInt(performance.now()*/*15*/1000_0.0|/*17*/0)if(!this.epoch1970PgNs7) // performance.timing.navigationStartthis.epoch1970PgNs7 = this.ns7Now_Date - epochPgNs7return epochPgNs7 + this.epoch1970PgNs7}static dateFromJd(jd) {return new Date((jd - 2440587.5) * 864e5)}static dateFromNs7(ns7) {return new Date(Number(ns7 - 0x1b2_1dd2_1381_4000n) / 1000_0.0)}static jdFromNs7(ns7) {   // atomic-clock leap-seconds (ignored)return 2440587.5 + (Number(ns7 - 0x1b2_1dd2_1381_4000n) / 864e9)}static ns7FromJd(jd) {return BigInt((jd - 2440587.5) * 864e9) + 0x1b2_1dd2_1381_4000n}static getRandomValues(va/*:<Uint32Array>*/) {if(typeof crypto !== 'undefined' && crypto.getRandomValues)crypto.getRandomValues(va)else for(let i = 0, n = va.length; i < n; i += 1)va[i] = Math.random() * 0x1_0000_0000 >>> 0}static get a4() {return this.a4_ || (this.a4_ = new Uint32Array(4))}static ntohl(v)/*:<BigInt>*/{let r = '0x', sign = 1n, s = BigInt(v).toString(16)if(s[0] == '-') s = s.substr(1), sign = -1nfor(let i = s.length; i > 0; i -= 2)r += (i == 1) ? ('0' + s[i-1]) : s[i-2] + s[i-1]return sign*BigInt(r)}static ntohl32(v)/*:<Number>*/{return Number(this.ntohl(v))}}

动机

虽然v4uuid定义了一个基本上随机的uuid,但希望有一个可以支持一些附加特征的uuid实现。

  • 快速有效地创建新的uuid(使用#1)
  • 实现为名义80 loc可读class的独立代码+评论
  • context中使用单调time合并uuid唯一性
  • 字符串化使得字符串形式:
    • 整理基于time,然后context(使用#2变式-2)
    • 转换回正确标识和恢复time的二进制形式
  • 在可用的情况下采用JavaScript微秒时钟精度
  • 支持基于julian-day的100纳秒单位的跨环境量子纪元年1582年10月15日,V1兼容性。启用统一时间的选择跨一系列环境和用例的行为符合EdgeSESS语言模型。

    特别适合与SQLite等设施一起使用数据库。

  • 使用es6 class设计来简化名义工作的可扩展性以扩展它提供其他uuid变体
  • 对于这个发布,统一并合并了基本的time相关eswc库API。
    • JulianDay API
    • NS7(100纳秒量子) API
    • ntohl用于endian方便重新排序的APIBigInt字符串表示
  • 源自QKS Smalltalk 1991,AOS®[敏捷对象系统;代理对象系统]它保留的语言、框架和运行时的引擎系列技术跨各种当前和历史主机操作系统的用例兼容性模型。
    • 特别是Xuid花括号引用的标量字符串格式支持guiduuiduid(#3、#4、#5)表示,FILETIME等。

      如:{1eb4a659-8bdc-4ce0-c002-b1d505d38ea8}

  • 最后但并非最不重要的是,它提供了一个理想的解决方案索引数据库object stores,其中使用uuid作为primaryKey变得令人向往。
    • 启用自动排序功能
    • 自然字符串排序规则
      • 注意微妙使用uuid备选案文-2来反转time链化形式的LHS。
    • 自然和简单的put更新
    • efs(EdgeS虚拟文件系统自动名称)的自然模式
    • service-workercloud-server同步和复制操作

总结

虽然简洁,希望现在这是足够的解释;试试看

并且,随时发表评论、提交反馈或建议。

当作为EdgeS Web客户端eswc图书馆的一部分在GitHub上发布时efs索引数据库使用模式将作为其示例设计意图,包括解决效率和可用性indexedDb相关 PWAsyncreplicate场景。

相关

基准测试uuid/sec

const start = Xuid.ns7Nowfor(let i = 100000; i; i -=1)Xuid.v4Newconst end = Xuid.ns7Nowconsole.log(`Delta 7ns: ${(end-start)/100000n}`)

导致:值为16…20=>~2微秒=>50万uuid/秒

这只是一个概念,当然可以在很多方面进行改进,但并不像我想象的那么慢。

一般来说,此代码包含十六进制编码的时间戳(以毫秒为单位)(由于某些黑客行为,它提供了12位数字,因此即使在2527-06-24之后代码也可以工作,但在5138-11-16之后不能工作),这意味着它是可排序的。它不是那么随机,它使用mac地址表示最后12位数字。第13个字母被硬编码为1,以保持它的可排序性。

之后,接下来的6位数字来自半随机字符串,其中第一位数字来自该毫秒上生成的记录计数,其他数字是随机生成的。该6位数字部分包含破折号和硬编码字母“a”,以保持记录可分类。

我知道这可以缩短,性能可以提高,但我对结果感到满意(除了MAC地址)。

currentNanoseconds = () => {return nodeMode ? process.hrtime.bigint() : BigInt(Date.now() * 1000000);}
nodeFindMacAddress = () => {// Extract MAC addressconst interfaces = require('os').networkInterfaces();let result = null;for (index in interfaces) {let entry = interfaces[index];entry.forEach(item => {if (item.mac !== '00:00:00:00:00:00') {result = '-' + item.mac.replace(/:/g, '');}});}return result;}
const nodeMode = typeof(process) !== 'undefined';let macAddress = nodeMode ? nodeFindMacAddress() : '-a52e99ef5efc';let startTime = currentNanoseconds();

let uuids = []; // Array for storing generated UUIDs, useful for testinglet currentTime = null; // Holds the last value of Date.now(), used as a base for generating the UUIDlet timePart = null; // Part of the UUID generated from Date.now()let counter = 0; // Used for counting records created at certain millisecondlet lastTime = null; // Used for resetting the record counter
const limit = 1000000;
for (let testCounter = 0; testCounter < limit; testCounter++) {let uuid = testMe();
if (nodeMode || testCounter <= 50) {uuids.push(uuid);}}
const timePassed = Number(currentNanoseconds() - startTime);
if (nodeMode) {const fs = require('fs');fs.writeFileSync('temp.txt', JSON.stringify(uuids).replace(/,/g, ',\n'));} else {console.log(uuids);}
console.log({operationsPerSecond: (1000 * limit / timePassed).toString() + 'm',nanosecondsPerCycle: timePassed / limit,milliSecondsPassed: timePassed / 1000000,microSecondsPassed: timePassed / 1000,nanosecondsPassed: timePassed});
function testMe() {currentTime = Date.now();let uuid = null; // Function result
if (currentTime !== lastTime) {// Added a 9 before timestamp, so that the hex-encoded timestamp is 12 digits long. Currently, it is 11 digits long, and it will be until 2527-06-24// console.log(Date.parse("2527-06-24").toString(16).length)// Code will stop working on 5138-11-17, because the timestamp will be 15 digits long, and the code only handles up to 14 digit timestamps// console.log((Date.parse("5138-11-17")).toString().length)timePart = parseInt(('99999999999999' + currentTime).substr(-14)).toString(16);timePart = timePart.substr(0, 8) + '-' + timePart.substr(8, 4) + '-1';counter = 0;}
randomPart = ('000000' + Math.floor(10 * (counter + Math.random()))).slice(-6);randomPart = randomPart.substr(0, 3) + '-a' + randomPart.substr(3, 3);uuid = timePart + randomPart + macAddress;
counter++;
lastTime = currentTime;
return uuid;}

下面是一个函数,它从字符串或随机UUID生成静态UUID,如果没有提供字符串:

function stringToUUID (str){if (str === undefined || !str.length)str = "" + Math.random() * new Date().getTime() + Math.random();
let c = 0,r = "";
for (let i = 0; i < str.length; i++)c = (c + (str.charCodeAt(i) * (i + 1) - 1)) & 0xfffffffffffff;
str = str.substr(str.length / 2) + c.toString(16) + str.substr(0, str.length / 2);for(let i = 0, p = c + str.length; i < 32; i++){if (i == 8 || i == 12 || i == 16 || i == 20)r += "-";
c = p = (str[(i ** i + p + 1) % str.length]).charCodeAt(0) + p + i;if (i == 12)c = (c % 5) + 1; //1-5else if (i == 16)c = (c % 4) + 8; //8-Belsec %= 16; //0-F
r += c.toString(16);}return r;}
console.log("Random       :", stringToUUID());console.log("Static [1234]:", stringToUUID("1234")); //29c2c73b-52de-4344-9cf6-e6da61cb8656console.log("Static [test]:", stringToUUID("test")); //e39092c6-1dbb-3ce0-ad3a-2a41db98778c

jsfiddle

使用Blob的单行解决方案。

window.URL.createObjectURL(new Blob([])).substring(31);

末尾的值(31)取决于URL的长度。


编辑:

一个更紧凑和通用的解决方案,如里诺戈所建议:

URL.createObjectURL(new Blob([])).substr(-36);

最简单的函数是:

function createGuid(){let S4 = () => Math.floor((1+Math.random())*0x10000).toString(16).substring(1);let guid = `${S4()}${S4()}-${S4()}-${S4()}-${S4()}-${S4()}${S4()}${S4()}`;   
return guid.toLowerCase();}

此产品从a-z、0-9返回5组8位数字其中大部分是随机的,但不包括一天中的时间,并具有随机递增的计数器。您可以指定任何您喜欢的基数(十六进制,十进制,36),默认情况下为每组8选择一个随机基数,在基数16到36的范围内

function newId(base) {return[Math.random,function (){ return (newId.last ? windowId.last + Math.random() : Math.random() ) },Math.random,Date.now,Math.random].map(function(fn){return fn().toString(base||(16+(Math.random()*20))).substr(-8);}).join('-');}
var demo = function(base){document.getElementById('uuid').textContent = newId(base);}demo(16);
#uuid { font-family: monospace; font-size: 1.5em; }
<p id="uuid"></p><button onclick="demo(16);">Hex (base 16)</button><button onclick="demo(36);">Base 36</button><button onclick="demo(10);">Decimal (base 10)</button><button onclick="demo();">Random base</button>

Broofa的回答的启发,我有自己的看法:

这是使用crypto.getRandomValues的加密更强大的版本。

function uuidv4() {const a = crypto.getRandomValues(new Uint16Array(8));let i = 0;return '00-0-4-1-000'.replace(/[^-]/g,s => (a[i++] + s * 0x10000 >> s).toString(16).padStart(4, '0'));}
console.log(uuidv4());

这是使用Math.random的更快版本,使用几乎相同的原理:

function uuidv4() {return '00-0-4-1-000'.replace(/[^-]/g,s => ((Math.random() + ~~s) * 0x10000 >> s).toString(16).padStart(4, '0'));}
console.log(uuidv4());

最酷的方式:

function uuid(){var u = URL.createObjectURL(new Blob([""]))URL.revokeObjectURL(u);return u.split("/").slice(-1)[0]}

它可能不是快速,高效或在IE2中支持,但它确实很酷

添加:v15.6.0,v14.17.0有一个内置的crypto.randomUUID()函数。

import * as crypto from "crypto";
const uuid = crypto.randomUUID();

在浏览器中,#0目前支持Chromium92+和Firefox 95+。

对于您已经通过使用#0方法为某些资源创建了URL的情况,您可能不会比以下操作更快或更短:

const uuid = url => url.substr(-36);

上述方法适用于任何符合createObjectURL的实现,因为规范明确要求UUID添加到前者返回的URL的末尾。所以你可以保证最后36个字符是生成URL的UUID部分。

需要明确的是,有时-见鬼,也许大多数时候,考虑到所有因素-您想要为使用createObjectURL创建URL的资源之外的其他资源生成UUID。在这种情况下,在某些new Blob()上调用后一种方法绝对会降低性能(并且泄漏内存,除非您使用相应的revokeObjectURL进行清理)。尽管如此,它仍然是一个相当“单行”的。

不要建议您使用上述方法仅用于生成UUID,除非您已经有通过createObjectURL获得的URL或末尾有UUID的东西。

我只是想提一下上面的变体的完整性。

我使用这个版本。它安全而简单。它不是生成格式化的uid,它只是生成您需要的随机字符字符串。

export function makeId(length) {let result = '';const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';const charactersLength = characters.length;
for (let i = 0; i < length; i++) {let letterPos = crypto.getRandomValues(new Uint8Array(1))[0] / 255 * charactersLength - 1result += characters[letterPos]}return result;}

这是一个通过random.org使用真随机生成RFC4122的方法。如果获取失败,它会回退到浏览器内置的crypto库,这应该几乎一样好。最后,如果用户的浏览器不支持,它使用Math.random()

async function UUID() {//get 31 random hex charactersreturn (await (async () => {let output;try {//try from random.orgoutput = (await (await fetch('https://www.random.org/integers/?num=31&min=0&max=15&col=31&base=16&format=plain&rnd=new')).text())//get rid of whitespace.replace(/[^0-9a-fA-F]+/g, '');if (output.length != 31)throw '';}catch {output = '';try {//failing that, try getting 16 8-bit digits from cryptofor (let num of crypto.getRandomValues(new Uint8Array(16)))//interpret as 32 4-bit hex numbersoutput += (num >> 4).toString(16) + (num & 15).toString(16);//we only want 31output = output.substr(1);}catch {//failing THAT, use Math.randomwhile (output.length < 31)output += (0 | Math.random() * 16).toString(16);}}return output;})())//split into appropriate sections, and set the 15th character to 4.replace(/^(.{8})(.{4})(.{3})(.{4})/, '$1-$2-4$3-$4-')//force character 20 to the correct range.replace(/(?<=-)[^89abAB](?=[^-]+-[^-]+$)/, (num) => ((parseInt(num, 16) % 4 + 8).toString(16)));}

输入图片描述

简单的uuid包很容易做到https://www.npmjs.com/package/uuid

const { v4: uuidv4 } = require('uuid');uuidv4(); // ⇨ '1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed'