提问



我试图在JavaScript中创建全局唯一标识符。我不确定所有浏览器上可用的例程,随机和内置随机数生成器的种子等等。


GUID/UUID应至少为32个字符,并应保持在ASCII范围内,以避免传递它们时出现问题。

最佳参考


在这方面有几次尝试。问题是:你想要实际的GUID,还是像一样的随机数字,如GUID?生成随机数很容易。


function guid() {
  function s4() {
    return Math.floor((1 + Math.random()) * 0x10000)
      .toString(16)
      .substring(1);
  }
  return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
}


但请注意,此类值不是正版GUID


<击>
没有办法在Javascript中生成真正的GUID,因为它们依赖于浏览器不公开的本地计算机的属性。您将需要使用像ActiveX这样的特定于操作系统的服务:http://p2p.wrox.com/topicindex/20339.htm
[101]


编辑:不正确 - RFC4122允许随机(版本4)GUID。有关细节,请参阅其他答案。


注意:提供的代码段不遵循RFC4122,后者要求必须将版本(4)集成到生成的输出字符串中。 如果您需要合规的GUID,请不要使用此答案


使用:


var uuid = guid();


演示:





function guid() {
  return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
    s4() + '-' + s4() + s4() + s4();
}

function s4() {
  return Math.floor((1 + Math.random()) * 0x10000)
    .toString(16)
    .substring(1);
}

document.getElementById('jsGenId').addEventListener('click', function() {
  document.getElementById('jsIdResult').value = guid();
})

input { font-family: monospace; }

<button id="jsGenId" type="button">Generate GUID</button>
<br>
<input id="jsIdResult" type="text" placeholder="Results will be placed here..." readonly size="40"/>



对于RFC4122版本4兼容的解决方案,这个单线程(ish)解决方案是我能想出的最紧凑的解决方案。:[102]




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



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


这里有一个类似的RFC4122版本4兼容解决方案,通过将前13个十六进制数字偏移时间戳的十六进制部分来解决该问题。这样,即使Math.random在同一种子上,两个客户端都必须生成UUID完全相同的毫秒(或10,000多年后)获得相同的UUID:[104]


function generateUUID() { // Public Domain/MIT
    var d = new Date().getTime();
    if (typeof performance !== 'undefined' && typeof performance.now === 'function'){
        d += performance.now(); //use high-precision timer if available
    }
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
        var r = (d + Math.random() * 16) % 16 | 0;
        d = Math.floor(d / 16);
        return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
    });
}




这是一个小小的测试。[105]

其它参考1


broofa的答案非常光滑,确实 - 令人印象深刻的聪明,真的... rfc4122兼容,有点可读,紧凑。太棒了!


但是如果你正在查看那个正则表达式,那些很多replace()回调,toString()Math.random()函数调用(他只使用4位结果并浪费了你可能会开始怀疑性能。事实上,joelpt甚至决定用generateQuickGUID抛弃RFC的通用GUID速度。


但是,我们可以获得速度和 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 that 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 [106] [107]


为了解释,让我们从broofa的代码开始:


'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);
});


所以用任意随机十六进制数字替换xy用随机数据(除了按RFC规则强制前2位到10,并且正则表达式不匹配[[-4字符,所以他不必处理它们。非常非常光滑。


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


性能的第一步是消除RegEx及其回调函数,而是使用简单的循环。这意味着我们必须处理-4字符,而布洛法则没有。另外,请注意我们可以使用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;
}


基本上,相同的内部逻辑,除了我们检查-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
}


这节省了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
}


下一个优化是另一个经典。由于我们只在每次循环迭代中处理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
}


我尝试了一次处理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];
}


模块化:http://jcward.com/UUID.js - UUID.generate() [108]


有趣的是,生成16字节的随机数据很容易。整个技巧是以符合RFC的字符串格式表达它,并且用16字节的随机数据,一个展开的循环和查找表来完成它。


我希望我的逻辑是正确的 - 在这种繁琐的工作中犯错是很容易的。但输出对我来说很好。我希望你通过代码优化享受这种疯狂的旅程!


建议:我的主要目标是展示和教授潜在的优化策略。其他答案涵盖了重要的主题,如冲突和真正的随机数,这对于生成良好的UUID非常重要。

其它参考2


这里有一些基于RFC 4122的代码,第4.4节(从真正的随机数或伪随机数创建UUID的算法)。[109]


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;
}

其它参考3


最快的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 (slavik@meltser.info).
 * @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位数(for
    例子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"),然后从第二个索引(第三个字符)中删除它,长度为8个字符。其余部分在这种情况下,添加零不会对结果造成伤害,因为它无论如何都会将其切断
    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

  • 为了区分这两种类型,我在对创建者函数_p8(s)中添加了一个标志参数,s参数告诉函数是否添加破折号。

  • 最后我们使用以下链接构建GUID:_p8() + _p8(true) + _p8(true) + _p8(),并将其返回。



在我的博客[110]上链接到这篇文章


享受! : - )

其它参考4


var uniqueId = Math.random().toString(36).substring(2) 
               + (new Date()).getTime().toString(36);


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


如果以较短的间隔生成两个ID,并假设随机方法是真正随机的,那么这将产生99.99999999999999%可能是全局唯一的ID(在10 ^ 15中的1中发生冲突)


您可以通过添加更多数字来增加此数字,但要生成100%唯一ID,您需要使用全局计数器。




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

<div id="unique">
</div>



以下是最高投票答案的组合,以及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-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
        // https://stackoverflow.com/questions/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上,如果你想测试它。[113]

其它参考5


这是一个完全不兼容但非常高性能的实现,用于生成类似于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');

其它参考6


这是2011年10月9日用户 jed 的评论,https://gist.github.com/982883:[114]的解决方案


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
      )
}

其它参考7


来自sagi shkedy的技术博客:[116]


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只提供足够大的密钥范围,以将碰撞的变化减少到几乎为零。[117]

其它参考8


Web服务很有用。


快速谷歌发现:http://www.hoskinson.net/GuidGenerator/[118]


不能保证这个实现,但有些人必须发布一个真正的GUID生成器。


使用这样的Web服务,您可以开发一个使用GUID Web服务的REST Web界面,并通过AJAX将其提供给浏览器中的javascript。

其它参考9


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时出现问题。

其它参考10


简单的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 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);
  });
};

其它参考11


你可以使用node-uuid(https://github.com/kelektiv/node-uuid)[119]


简单,快速生成RFC4122 UUIDS。[120]


特征:



  • 生成RFC4122版本1或版本4 UUID

  • 在node.js和浏览器中运行。

  • 支持平台上的密码强随机#生成。

  • 占地面积小(想要更小的东西吗?看看这个!)






使用NPM安装: [121]


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 [122] [123]





想要更小?看看这个:https://gist.github.com/jed/982883 [124]





用法:


// 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'

其它参考12


从好的维基百科那里链接到UUID的javascript实现。[125]


它看起来相当优雅,也许可以通过使用客户端IP地址的哈希来改进。这个哈希也许可以插入到html文档服务器端供客户端javascript使用。


更新:原始网站有一个shuffle,这是更新版本[126]

其它参考13


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



   UUID.js适用于JavaScript的符合RFC的UUID生成器。

  
  参见RFC 4122 http://www.ietf.org/rfc/rfc4122.txt。[128]

  
  功能生成符合RFC 4122的UUID。

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

  
  UUID对象允许对UUID的各种访问,包括访问
  UUID字段。

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


其它参考14


这创建版本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

其它参考15


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

其它参考16


调整了我自己的UUID/GUID生成器,其中包含一些额外内容。[129]


我使用下面的Kybos随机数生成器来加密声音。[130]


下面是我的脚本,其中包含来自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));

其它参考17


更好的方法:


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}

其它参考18


我想了解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);
        }
    );
};

其它参考19


这只是一个简单的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.");
    }
);

其它参考20


对于那些想要符合rfc4122版本4的解决方案而需要速度考虑的人(很少调用Math.random()):


function UUID() {
    var nbr, randStr = "";
    do {
        randStr += (nbr = Math.random()).toString(16).substr(2);
    } 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)
        ].join("");
}


上述功能应该在速度和随机性之间保持适当的平衡。

其它参考21


我知道,这是一个老问题。为了完整起见,如果您的环境是SharePoint,则会有一个名为SP.Guid.newGuid(msdn link)的实用程序函数,它会创建一个新的guid。此函数位于sp.init.js文件中。如果你重写这个函数(从其他私有函数中删除一些其他依赖项),它看起来像这样:[131]


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;
};

其它参考22


有一个jQuery插件可以很好地处理Guid!@ http://plugins.jquery.com/project/GUID_Helper [132]


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。

其它参考23


ES6样本


const guid=()=> {
  const s4=()=> Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);     
  return `${s4() + s4()}-${s4()}-${s4()}-${s4()}-${s4() + s4() + s4()}`;
}

其它参考24


这个基于日期,并添加随机后缀以确保唯一性。
适用于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;
        };

其它参考25


奇怪的是,没有人提到这一点,但为了完整性,npm上有大量的guid生成器我愿意打赌他们中的大多数也在浏览器中工作。[133]