我正尝试在 JavaScript 中创建全局唯一标识符。我不敢肯定什么例程可在所有浏览器如何"随机"上,播种的内置的随机数字生成器,等等...

GUID / UUID 应至少 32 个字符,应保持在 ASCII 范围来传递它们时避免麻烦。

2008-09-20 15:00:47
问题评论:

当 repesented 作为字符串至少 36 并没有超过 38 个字符的长度,与模式匹配的 Guid ^ {? [a-zA-Z0-9] {36}?$ 因此始终是 ascii。

David Bau 提供更好、 seedable 随机数生成器在davidbau.com/archives/2010/01/30/...我编写了稍有另一种在blogs.cozi.com/tech/2010/04/generating-uuids-in-javascript.html生成 Uuid

回答:

已经在这几个尝试。问题是︰ 是否实际 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编辑︰ 不正确-RFC4122 允许随机 ("版本 4") 的 id。其他答案有关的详细信息,请参阅。

使用︰

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" onclick="runGuid">Generate GUID</button>
<br>
<input id="jsIdResult" type="text" placeholder="Results will be placed here..." readonly size="40"/>

实际上,RFC 允许创建的随机数字的 Uuid。您只需 twiddle 的几位来这种情况下对其进行标识。请参见部分 4.4。从真正的随机或伪随机编号创建一个 UUID 算法︰ rfc-archive.org/getrfc.php?rfc=4122

在 Chrome 中此代码并不总是生成正确大小的 GUID。35 和 36 之间变化的长度

如此明显的错误答案如何获得如此多的 upvotes?即使代码不正确 (没有意外情况发生,您显然不了解有关的 Guid 时),因为在适当的位置不存在 4。en.wikipedia.org/wiki/Globally_unique_identifier

此答案是错误的。请使用此代码。随机算法可以生成而不是 4 的 2 或 3 数字的序列随机算法生成一个小的数字。

当已删除"1 +..."和"substring(1)"在版本 5 中断开此答案。这些位保证一致的长度。

对于 rfc4122 版本 4 兼容解决方案,此 one-liner(ish) 解决方案是最紧凑,我无法拿出。:

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

例如︰

>>> '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);});
"3bce4931-6c75-41ab-afe0-2ec108a30860"

[更新,2015年-06-02︰ 请注意 UUID 唯一性严重依赖基础随机数生成器 (RNG)。上面的解决方案在但是Math.random()是为了简单起见,使用Math.random() 并非一定要高质量 RNG。Adam Hyland优秀 writeup Math.random() 上的详细信息,请参阅。更为可靠的解决方案,请考虑节点 uuid.js类似 [免责声明︰ 我是作者],它可使用高质量 RNG Api。]

[更新,2015年-08-26 日︰ 作为边注,此概要介绍了如何确定多少 Id 可以生成之前到达某些冲突的概率。例如,对于 3.26x1015版本 4 RFC4122 Uuid 您有 1000000 中发生冲突的机会。

是它可以安全地使用此代码来生成客户端唯一 id,然后作为主键使用这些 id 来将对象保存在服务器上?

...(续)生成的此函数冲突的两个 Id 的几率是,确切地说,小 astronomically。除 ID 128 位 6 随机生成的这意味着对于任何两个 id,2 中 1 ^ ^122 (或 5.3 x 10 ^ ^36) 它们将发生冲突的机会。

我发布关于冲突stackoverflow.com/questions/6906916/...的问题

@Muxa 的问题的答案肯定是否?就永远不会真正的安全信任来自客户端的内容。我猜它取决于如何可能在您的用户是 javascript 控制台显示并手动将此变量更改成他们想要这样。或者,他们可能只是回您所需的 id。它将还取决于是否将用户选择他们自己的 ID 会导致安全漏洞。无论如何,进入表一个随机数字 ID 时,我将可能会生成服务器端,因此我知道我有过程控制。

@DrewNoakes-Uuid 不是完全随机的 # 字符串 ' s。"4"是 uuid 版本 (4 ="随机")。"Y"标记的位置 uuid variant 类型的值 (字段布局,基本上) 需要嵌入。请参见 4.1.1 和 4.1.3 ietf.org/rfc/rfc4122.txt以获取详细信息的部分。

我很喜欢如何干净Broofa 的回答是,但很不幸的Math.random差实现保留发生碰撞的机会。

下面是类似RFC4122版本 4 兼容的解决方案的偏移前 13 16 进制的数字时间戳的十六进制部分解决了该问题。这样一来,即使Math.random相同的种子,在两个客户端必须生成完全相同的毫秒 (或 10000 多年以后) 来获取同一个 UUID UUID:

function generateUUID(){
    var d = new Date().getTime();
    if(window.performance && typeof window.performance.now === "function"){
        d += performance.now(); //use high-precision timer if available
    }
    var uuid = '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);
    });
    return uuid;
}


这里是不错可供测试。

请记住,new Date().getTime()不是更新每毫秒。我不确定这将如何影响您的算法的预期的随机性。

我认为这是最佳的答案只是因为它在其中使用日期的生成。但是如果您有现代浏览器堆建议Date.now()new Date().getTime()

performance.now会更好一些。与 Date.now, performance.now()返回的时间戳并不局限于一个毫秒的分辨率。相反,它们代表了时间以浮点数形式带到微秒的精度此外与 Date.now,performance.now()始终以恒定速率增加,独立的系统时钟可能手动调整或扭曲的软件 (如网络时间协议) 返回的值。

您可能应该已经链接到 MDN 或另一个显示实际的文档并不填充代码;) @daniellmb

FYI,每个站点脚注,站点上的所有用户贡献都有 cc 的 sa 3.0 许可。

broofa 的答案是这太妙了,事实上-极其聪明,实际上...rfc4122 符合要求,某种程度上可读的和紧凑。超 !

但是,如果您正在查看该正则表达式,这些许多replace()回调, toString()Math.random() (他是只使用 4 位结果的浪费的其余部分) 函数调用,您可能会想知道性能。事实上,即使决定扔掉 rfc 一般 GUID 速度,请使用generateQuickGUID的 joelpt.

,但我们可以得到速度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.

因此通过优化我第六次迭代,我击败了最受欢迎的答案对12 X9 X,通过接受的答案通过和快速-不符合答案2-3倍。和我仍是符合标准的 rfc4122。

如何有兴趣?我本来面目的完整源代码,在http://jsfiddle.net/jcward/7hyaC/3/http://jsperf.com/uuid-generator-opt/4

有关说明,让我们开始用 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);
});

因此它用任何随机的十六进制数字,用随机的数据 (除强制顶部 2 位到10每个 rfc 规范), y替换x和正则表达式不匹配-4个字符,因此他不必处理它们。非常、 非常棒。

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

性能的第一步是消除正则表达式和其回调函数,并使用简单的循环来代替。这意味着我们必须处理的-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;
}

基本上相同的内部逻辑,但对- ,我们检查或4和循环结构 (而不是替换回调) 获取我们提高几乎 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 兼容的位的位置,但并不太难。我们需要做出更大的查找表 (16 x 16 或 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 和 tanked 的性能。但与指定预先的随机数据的四个变量,然后使用查阅表格中,并应用适当的 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];
}

Modualized: http://jcward.com/UUID.js - UUID.generate()

有趣的事情是,产生 16 字节的随机数据是很容易的一部分。整个技巧表达它以字符串格式与 RFC 相符,并且最紧密来完成 16 个字节的随机数据,展开的循环和查阅表格。

我希望我的逻辑是正确的-它是很容易在这种乏味的位工作出现失误。但输出看起来对我很好。我希望您喜欢通过代码优化此悲伤持续一段时间 !

注意︰我的主要目标是显示和教潜在优化策略。其他答案包括以下重要主题︰ 冲突和真正的随机数字,重要用于生成好的 Uuid。

jsperf.com将允许您捕获数据并查看跨浏览器和设备的结果。

Hi @chad,很好的问题。k 无法移动之外,但,没有改善上述性能,并使范围 messier。并构建数组和奇怪联接返回删除的性能。但同样,随意尝试 !

超的答案 !我只是需要一个快速的函数来生成一个随机的十六进制字符串,并且修改 e7 以满足我的需要。谢谢 !

我会很想看到节点 uuid.js 的进行比较。那里的工作中我开始了更多的性能,重点是一些的保持。但是,我已经因为备份之外,使用具有多个可读/可维护的代码。原因是该 uuid 性能不只是在现实世界中的问题。Uuid 通常被创建结合得多慢操作 (例如使网络请求,创建和保持一个模型对象),其中只 shaving 几个微秒关闭的事情并不重要。

此代码仍包含了几个错误︰ Math.random()*0xFFFFFFFF行应Math.random()*0x100000000为完全的随机性和>>>0应使用而不是|0保留未签名的 (虽然与当前的代码,我认为它走获取确定,即使它们已被签名) 的值。最后它是非常好的主意这些天如果可用,请以及回退到 Math.random 仅在绝对必要时,请使用window.crypto.getRandomValuesMath.random 可能会低于 128 位的熵,在这种情况下这将更容易受到冲突比实际所需。

下面是一些代码根据RFC 4122,部分 4.4 (从真正的随机或 Pseudo-Random 号创建一个 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;
}

这不会产生所需的 c# 到 System.Guid 分析短划线。它将呈现如下︰ B42A153F1D9A4F92990392C11DD684D2,当它应该呈现类似︰ B42A153F-1D9A-4F92-9903-92C11DD684D2

从规范 ABNF 不会包含"-"字符,因此我更新为兼容。

我个人不喜欢短划线,但于每个他们自己。嘿这就是为什么我们的程序员 !

应将该数组声明事先不生成 GUID 时动态调整大小。var s = new Array(36);

@Levitikon。NET 的Guid.Parse()应分析B42A153F1D9A4F92990392C11DD684D2成一个 Guid 不错。它不需要包含连字符。

内容来源于Stack Overflow Create GUID / UUID in JavaScript?
请输入您的翻译

Create GUID / UUID in JavaScript?

确认取消