【问题标题】:How to create a GUID / UUID如何创建 GUID/UUID
【发布时间】:2026-02-03 23:00:02
【问题描述】:

我正在尝试在 JavaScript 中创建全局唯一标识符。我不确定所有浏览器上都有哪些例程,内置随机数生成器的“随机性”和播种程度等。

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

【问题讨论】:

  • GUID 以字符串形式表示时,长度至少为 36 且不超过 38 个字符,并且匹配模式 ^\{?[a-zA-Z0-9]{36}?\}$因此总是 ascii。
  • David Bau 在davidbau.com/archives/2010/01/30/… 提供了一个更好的、可播种的随机数生成器我在blogs.cozi.com/tech/2010/04/generating-uuids-in-javascript.html 写了一个稍微不同的生成 UUID 的方法
  • 奇怪的是还没有人提到这一点,但为了完整起见,有很多 guid generators on npm 我敢打赌他们中的大多数也可以在浏览器中工作。
  • 如果有人想要更多选项,例如不同版本的 uuid 和非标准 guid 支持,像这些 [fungenerators.com/api/uuid] 这样的基于 REST 的 uuid 生成服务也是一个有吸引力的选择。
  • 大约 12 年后,使用 BigInt 和 ES6 类,可以实现 500,000 uuid/sec 的其他技术。 See reference

标签: javascript guid uuid


【解决方案1】:

[于 2021 年 10 月 16 日编辑,以反映生产符合 RFC4122 的 UUID 的最新最佳实践]

这里的大多数读者都想使用the uuid module。它经过了良好的测试和支持。

crypto.randomUUID() 函数是Node.jsan increasing number of browsers 支持的新兴标准。

如果这些都不适合你,有这个方法(基于这个问题的原始答案):

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

注意:强烈建议不要使用依赖于 Math.random() 的 任何 UUID 生成器(包括此答案以前版本中的 sn-ps) reasons best-explained here。 TL;DR:基于 Math.random() 的解决方案不能提供良好的唯一性保证。

【讨论】:

  • @Muxa 问题的答案肯定是“不”吗?相信来自客户的东西从来都不是真正安全的。我想这取决于您的用户打开 javascript 控制台并手动将变量更改为他们想要的东西的可能性。或者他们可以将您想要的 id 发回给您。这还取决于用户选择自己的 ID 是否会导致漏洞。无论哪种方式,如果它是一个进入表的随机数 ID,我可能会在服务器端生成它,以便我知道我可以控制该过程。
  • @DrewNoakes - UUID 不仅仅是一串完全随机的#。 “4”是 uuid 版本(4 =“随机”)。 “y”标记需要嵌入 uuid 变体(基本上是字段布局)的位置。有关详细信息,请参阅ietf.org/rfc/rfc4122.txt 的第 4.1.1 和 4.1.3 节。
  • 我知道你已经在你的帖子中添加了很多警告,但你最好现在就删除第一个答案,很多菜鸟都会来到这个答案并复制第一个他们看到的东西没有阅读其余部分。实际上you can't reliably generate UUIDs from the Math.random API 依赖它会很危险。
  • 如果你真的想要保持版本控制内联,而不是落后于修订历史,你必须颠倒顺序:保持最新的答案作为第一。跨度>
  • 我有点困惑,在javascript中[1e7]+-1e3并不真正意味着什么,一个数组被添加到一个数字?我错过了什么?注意:在打字稿中它没有通过
【解决方案2】:

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

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

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

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

【讨论】:

  • 实际上,RFC 允许从随机数创建的 UUID。你只需要旋转几位就可以识别它。请参阅第 4.4 节。从真正随机或伪随机数创建 UUID 的算法:rfc-archive.org/getrfc.php?rfc=4122
  • 这不应该是公认的答案。它实际上并没有回答这个问题 - 而是鼓励导入 25,000 行代码,以便在任何现代浏览器中使用一行代码即可完成。
  • @AbhiBeckert 答案来自 2008 年,对于 node.js 项目,选择更多的依赖项而不是项目大小可能是有效的
  • @Phil 这是一个“高度活跃的问题”,这意味着它应该有一个带有绿色勾号的优秀答案。不幸的是,事实并非如此。这个答案没有错或不正确(如果有,我会编辑答案)-但下面还有另一个更好的答案,我认为它应该位于列表的顶部。这个问题也特别与浏览器中的javascript有关,而不是node.js。
  • 我对 Math.random 随机性质量低的说法提出质疑。 v8.dev/blog/math-random。如您所见,它通过了一个很好的测试套件,并且 v8、FF 和 Safari 使用了相同的算法。并且 RFC 规定,UUID 可以接受伪随机数
【解决方案3】:

我真的很喜欢Broofa's answer 的干净程度,但不幸的是poor implementations of Math.random 留下了碰撞的机会。

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

function generateUUID() { // Public Domain/MIT
    var d = new Date().getTime();//Timestamp
    var d2 = ((typeof performance !== 'undefined') && performance.now && (performance.now()*1000)) || 0;//Time in microseconds since page-load or 0 if unsupported
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = Math.random() * 16;//random number between 0 and 16
        if(d > 0){//Use timestamp until depleted
            r = (d + r)%16 | 0;
            d = Math.floor(d/16);
        } else {//Use microseconds since page-load if supported
            r = (d2 + r)%16 | 0;
            d2 = Math.floor(d2/16);
        }
        return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
    });
}

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>

Here's a fiddle to test.


ES6 的现代化 sn-p

const generateUUID = () => {
  let
    d = 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>

【讨论】:

  • 请记住,new Date().getTime() 不会每毫秒更新一次。我不确定这会如何影响算法的预期随机性。
  • performance.now 会更好。与 Date.now 不同,performance.now() 返回的时间戳不限于一毫秒的分辨率。相反,它们将时间表示为精度高达微秒的浮点数。与 Date.now 不同的是,performance.now() 返回的值总是以恒定速率增加,与系统时钟无关,系统时钟可能会手动调整或被网络时间协议等软件倾斜。
  • 实际时间分辨率可能是也可能不是 17 毫秒(1/60 秒),而不是 1 毫秒。
  • Crypto.getRandomValues 会解决 Math.random 的主要问题吗??
  • @NaveenReddyMarthala Node.js 默认以严格模式运行 JavaScript,不幸的是,这不允许布尔逻辑运算符简写检查 undefined 变量的真实性。要解决此问题,请尝试在更新版本中将 var d2 = (performance .. 替换为 var d2 = (typeof performance !== 'undefined' ..。另一种选择(实际上将利用 Node.js 提高的性能精度而不是丢弃它)是在您的要求中重新添加 const { performance } = require('perf_hooks');
【解决方案4】:

broofa's answer 非常漂亮,确实 - 非常聪明,真的... 符合 RFC4122,可读性强且紧凑。太棒了!

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

但是,我们能否获得速度 RFC 合规性?我说,是的!我们可以保持可读性吗?嗯......不是真的,但如果你跟着它很容易。

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

                  Desktop   Android
           broofa: 1617ms   12869ms
               e1:  636ms    5778ms
               e2:  606ms    4754ms
               e3:  364ms    3003ms
               e4:  329ms    2015ms
               e5:  147ms    1156ms
               e6:  146ms    1035ms
               e7:  105ms     726ms
             guid:  962ms   10762ms
generateQuickGuid:  292ms    2961ms
  - Note: 500k iterations, results will vary by browser/CPU.

所以在我的第 6 次优化迭代中,我击败了最受欢迎的答案超过 12 次​​strong>,超过了接受的答案 9 次,以及快速非2-3 次。而且我仍然符合 RFC 4122。

对如何感兴趣?我已将完整的源代码放在 http://jsfiddle.net/jcward/7hyaC/3/http://jsperf.com/uuid-generator-opt/4

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

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

console.log(broofa())

所以它用任何随机十六进制数字替换x,用随机数据替换y(除了根据RFC规范强制最高两位为10),并且正则表达式不匹配-4 字符,所以他不必处理它们。非常非常流畅。

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

实现性能的第一步是消除 RegEx 及其回调函数,并改用简单的循环。这意味着我们必须处理 -4 字符,而 broofa 没有。另外,请注意,我们可以使用字符串数组索引来保持他光滑的字符串模板架构:

function e1() {
    var u='',i=0;
    while(i++<36) {
        var c='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'[i-1],r=Math.random()*16|0,v=c=='x'?r:(r&0x3|0x8);
        u+=(c=='-'||c=='4')?c:v.toString(16)
    }
    return u;
}

console.log(e1())

基本上,相同的内部逻辑,除了我们检查-4,并使用while 循环(而不是replace() 回调)使我们提高了近3 倍!

下一步是桌面上的一个小步骤,但在移动设备上会有很大的不同。让我们进行更少的 Math.random() 调用并利用所有这些随机位,而不是将其中的 87% 丢弃在每次迭代中移出的随机缓冲区中。让我们也将模板定义移出循环,以防万一它有帮助:

function e2() {
    var u='',m='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx',i=0,rb=Math.random()*0xffffffff|0;
    while(i++<36) {
        var c=m[i-1],r=rb&0xf,v=c=='x'?r:(r&0x3|0x8);
        u+=(c=='-'||c=='4')?c:v.toString(16);rb=i%8==0?Math.random()*0xffffffff|0:rb>>4
    }
    return u
}

console.log(e2())

这可以为我们节省 10-30%,具体取决于平台。不错。但是下一个重要的步骤是通过一个经典的优化方法——查找表完全摆脱了 toString 函数调用。一个简单的 16 元素查找表将在更短的时间内执行 toString(16) 的工作:

function e3() {
    var h='0123456789abcdef';
    var k='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
    /* same as e4() below */
}
function e4() {
    var h=['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'];
    var k=['x','x','x','x','x','x','x','x','-','x','x','x','x','-','4','x','x','x','-','y','x','x','x','-','x','x','x','x','x','x','x','x','x','x','x','x'];
    var u='',i=0,rb=Math.random()*0xffffffff|0;
    while(i++<36) {
        var c=k[i-1],r=rb&0xf,v=c=='x'?r:(r&0x3|0x8);
        u+=(c=='-'||c=='4')?c:h[v];rb=i%8==0?Math.random()*0xffffffff|0:rb>>4
    }
    return u
}

console.log(e4())

下一个优化是另一个经典。由于我们在每次循环迭代中只处理 4 位输出,因此让我们将循环数减半并在每次迭代中处理 8 位。这很棘手,因为我们仍然必须处理符合 RFC 的位位置,但这并不太难。然后我们必须创建一个更大的查找表(16x16 或 256)来存储 0x00 - 0xFF,并且我们只在 e5() 函数之外构建它一次。

var lut = []; for (var i=0; i<256; i++) { lut[i] = (i<16?'0':'')+(i).toString(16); }
function e5() {
    var k=['x','x','x','x','-','x','x','-','4','x','-','y','x','-','x','x','x','x','x','x'];
    var u='',i=0,rb=Math.random()*0xffffffff|0;
    while(i++<20) {
        var c=k[i-1],r=rb&0xff,v=c=='x'?r:(c=='y'?(r&0x3f|0x80):(r&0xf|0x40));
        u+=(c=='-')?c:lut[v];rb=i%4==0?Math.random()*0xffffffff|0:rb>>8
    }
    return u
}

console.log(e5())

我尝试了一次处理 16 位的 e6(),仍然使用 256 元素 LUT,它显示了优化的收益递减。虽然它的迭代次数更少,但内部逻辑因处理量的增加而变得复杂,它在台式机上的执行情况相同,在移动设备上仅快 10%。

要应用的最终优化技术 - 展开循环。由于我们循环的次数是固定的,因此我们可以在技术上手动将其全部写出来。我用一个随机变量r 尝试过一次,我一直在重新分配它,结果性能下降了。但是,四个变量预先分配了随机数据,然后使用查找表,并应用适当的 RFC 位,这个版本将它们全部抽出来:

var lut = []; for (var i=0; i<256; i++) { lut[i] = (i<16?'0':'')+(i).toString(16); }
function e7()
{
    var d0 = Math.random()*0xffffffff|0;
    var d1 = Math.random()*0xffffffff|0;
    var d2 = Math.random()*0xffffffff|0;
    var d3 = Math.random()*0xffffffff|0;
    return lut[d0&0xff]+lut[d0>>8&0xff]+lut[d0>>16&0xff]+lut[d0>>24&0xff]+'-'+
    lut[d1&0xff]+lut[d1>>8&0xff]+'-'+lut[d1>>16&0x0f|0x40]+lut[d1>>24&0xff]+'-'+
    lut[d2&0x3f|0x80]+lut[d2>>8&0xff]+'-'+lut[d2>>16&0xff]+lut[d2>>24&0xff]+
    lut[d3&0xff]+lut[d3>>8&0xff]+lut[d3>>16&0xff]+lut[d3>>24&0xff];
}

console.log(e7())

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

有趣的是,生成 16 字节的随机数据是很容易的部分。整个技巧是用符合 RFC 的 string 格式来表达它,并且最紧密地使用 16 字节的随机数据、展开的循环和查找表来完成。

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

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

【讨论】:

  • 这段代码仍然包含一些错误:Math.random()*0xFFFFFFFF 行应该是Math.random()*0x100000000 以获得完全随机性,并且应该使用&gt;&gt;&gt;0 而不是|0 以保持值无符号(尽管使用当前的代码,我认为即使它们已签名,也可以解决)。最后,如果可用,现在使用window.crypto.getRandomValues 是一个非常好的主意,只有在绝对必要时才回退到 Math.random。 Math.random 的熵可能少于 128 位,在这种情况下,这将比必要的更容易受到碰撞。
  • 我只能说 - 我无法计算我向开发人员指出这个答案的次数,因为它非常漂亮地指出了性能、代码优雅和可读性之间的权衡。谢谢杰夫。
  • 我不知道自这些测试运行以来@Broofa 的答案是否发生了变化(或者运行测试的浏览器引擎是否发生了变化——已经五年了),但我只是将它们都运行在两种不同的基准测试服务(jsben.ch 和 jsbench.github.io),在每种情况下 Broofa 的答案(使用 Math.random)都比这个 e7() 版本快 30 - 35%。
  • @Andy 是对的。 Broofa 的代码截至 2021 年 8 月更快。我实施了 Dave 的建议并自己运行了测试。但我不认为差异在生产中应该那么重要:jsbench.github.io/#80610cde9bc93d0f3068e5793e60ff11
  • 我觉得你的比较可能是不公平的,因为 broofa 的回答似乎是针对 e4 UUID 的,并且你在此处针对 Ward 的 e7 实现进行了测试。当您将 broofa 的答案与此处提供的 e4 版本进行比较时,这个答案会更快。
【解决方案5】:

用途:

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,并假设随机方法是真正随机的,则生成的 ID 有 99.99999999999999% 的可能性是全局唯一的(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 的字母。在其他差异中,它是一些随机数字短。 (如果需要,可以添加更多随机数字)好处是这真的很快:) 你可以test validity of your GUID here

【讨论】:

  • 这不是 UUID 吗?
  • 没有。 UUID/GUID 是一个 122 位(+ 6 个保留位)的数字。它可能通过全局计数器服务来保证唯一性,但通常它会按时间、MAC 地址和随机性进行中继。 UUID 不是随机的!我在这里建议的 UID 没有完全压缩。您可以将其压缩为 122 位整数,添加 6 个预定义位和额外的随机位(删除一些计时器位),最终得到一个完美形成的 UUID/GUID,然后您必须将其转换为十六进制。对我来说,除了遵守 ID 的长度之外,这并没有真正增加任何内容。
  • 在虚拟机上中继 MAC 地址以获得唯一性是个坏主意!
  • 我做这样的事情,但带有前导字符和一些破折号(例如[slug, date, random].join("_") 来创建usr_1dcn27itd_hj6onj6phr。它使 id 也兼作“创建于”字段
  • 基于@SephReed 的评论,我认为将日期部分放在首位很好,因为它按时间顺序排序,如果存储或索引 ID,这可能会在以后提供好处。
【解决方案6】:

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

function createUUID() {
    // http://www.ietf.org/rfc/rfc4122.txt
    var s = [];
    var hexDigits = "0123456789abcdef";
    for (var i = 0; i < 36; i++) {
        s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
    }
    s[14] = "4";  // bits 12-15 of the time_hi_and_version field to 0010
    s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);  // bits 6-7 of the clock_seq_hi_and_reserved to 01
    s[8] = s[13] = s[18] = s[23] = "-";

    var uuid = s.join("");
    return uuid;
}

【讨论】:

  • 您应该事先声明数组大小,而不是在构建 GUID 时动态调整其大小。 var s = new Array(36);
  • 我认为在将clock_seq_hi_and_reserved的位6-7位设置为01的行中有一个非常小的错误。因为s[19]是一个字符'0'..'f'而不是整数 0x0..0xf, (s[19] & 0x3) | 0x8 不会随机分布——它会倾向于产生更多的 '9' 而更少的 'b'。仅当您出于某种原因关心随机分布时,这才会有所不同。
【解决方案7】:

这是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) 得到一个八进制的字符串 长字符。 (Math.random().toString(16).substr(2,8)
  • 有时Math.random() 函数会返回 较短的数字(例如0.4363),因为末尾有零(从上面的示例来看,实际上数字是0.4363000000000000)。这就是为什么我要附加到这个字符串 "000000000"(一个有 9 个零的字符串),然后用 substr() 函数将其截断,使其正好是 9 个字符(在右侧填充零)。
  • 恰好添加 9 个零的原因是因为更糟糕的情况,即 Math.random() 函数将返回正好 0 或 1(每个它们的概率为 1/10^16)。这就是为什么我们需要向它添加九个零("0"+"000000000""1"+"000000000"),然后将其从第二个索引(第三个字符)中删除,长度为八个字符。对于其余情况,添加零不会损害结果,因为无论如何它都会将其切断。 Math.random().toString(16)+"000000000").substr(2,8)

大会:

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

Link to this post on my blog

尽情享受吧! :-)

【讨论】:

  • 这个实现不正确。 GUID 的某些字符需要特殊处理(例如,第 13 位需要是数字 4)。
【解决方案8】:

这是一个完全不合规但非常高效的实现,用于生成一个 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');

【讨论】:

    【解决方案9】:

    来自sagi shkedy's technical blog

    function generateGuid() {
      var result, i, j;
      result = '';
      for(j=0; j<32; j++) {
        if( j == 8 || j == 12 || j == 16 || j == 20)
          result = result + '-';
        i = Math.floor(Math.random()*16).toString(16).toUpperCase();
        result = result + i;
      }
      return result;
    }
    

    还有其他涉及使用ActiveX 控件的方法,但请远离这些!

    我认为值得指出的是,没有 GUID 生成器可以保证唯一的键(检查 Wikipedia article)。总是有碰撞的可能。 GUID 只是提供足够大的键域来将冲突的变化减少到几乎为零。

    【讨论】:

    • 请注意,这不是技术意义上的 GUID,因为它无法保证唯一性。根据您的应用程序,这可能重要也可能不重要。
    • 关于性能的简要说明。此解决方案总共创建 36 个字符串以获得单个结果。如果性能至关重要,请考虑创建一个数组并按照以下人员的建议加入:tinyurl.com/y37xtx 进一步研究表明这可能无关紧要,因此 YMMV:tinyurl.com/3l7945
    • 关于唯一性,值得注意的是版本 1,3 和 5 UUID 是确定性的,而版本 4 则不是。如果这些 uuid 生成器的输入——v1 中的节点 id、v3 和 v5 中的命名空间和名称——是唯一的(因为它们应该是唯一的),那么生成的 UUID 就是唯一的。理论上,无论如何。
    • 这些 GUID 无效,因为它们没有指定 ITU-T | 要求的版本和变体。 ISO 推荐。
    • @DanielMarschall,这不会产生 UUID,但会产生有效的 GUID,这些 GUID 在 2008 年编写此答案时在 Microsoft 代码(例如 .Net)中很常见。请注意,这也是十六进制字符被强制为大写的原因。见:docs.microsoft.com/en-us/windows/win32/msi/guid
    【解决方案10】:

    这是top voted answer 的组合,以及Chrome's collisions 的解决方法:

    generateGUID = (typeof(window.crypto) != 'undefined' &&
                    typeof(window.crypto.getRandomValues) != 'undefined') ?
        function() {
            // If we have a cryptographically secure PRNG, use that
            // https://*.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://*.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);
            });
        };
    

    如果你想测试它是on jsbin

    【讨论】:

    • 请注意,第一个版本是`window.crypto.getRandomValues, does not keep the Version 4 UUIDs format defined by RFC 4122. That is instead of xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`产生xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
    【解决方案11】:

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

    这实现了与current highest-rated answer 相同的目标,但通过利用强制、递归和指数表示法减少了 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
          )
    }
    

    【讨论】:

      【解决方案12】:

      您可以使用node-uuid。它提供了简单、快速的RFC4122 UUIDS 生成。

      特点:

      • 生成 RFC4122 版本 1 或版本 4 UUID
      • Node.js 和浏览器中运行。
      • 支持平台上的加密强随机 # 生成。
      • 占地面积小(想要更小的东西?Check this out!

      使用 NPM 安装:

      npm install uuid
      

      或通过浏览器使用 uuid:

      下载原始文件 (uuid v1):https://raw.githubusercontent.com/kelektiv/node-uuid/master/v1.js 下载原始文件(uuid v4):https://raw.githubusercontent.com/kelektiv/node-uuid/master/v4.js


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


      用法:

      // Generate a v1 UUID (time-based)
      const uuidV1 = require('uuid/v1');
      uuidV1(); // -> '6c84fb90-12c4-11e1-840d-7b25c5ee775a'
      
      // Generate a v4 UUID (random)
      const uuidV4 = require('uuid/v4');
      uuidV4(); // -> '110ec58a-a0f2-4ac4-8393-c866d813b8d1'
      
      // Generate a v5 UUID (namespace)
      const uuidV5 = require('uuid/v5');
      
      // ... using predefined DNS namespace (for domain names)
      uuidV5('hello.example.com', v5.DNS)); // -> 'fdda765f-fc57-5604-a269-52a7df8164ec'
      
      // ... using predefined URL namespace (for, well, URLs)
      uuidV5('http://example.com/hello', v5.URL); // -> '3bbcee75-cecc-5b56-8031-b6641c1ed1f1'
      
      // ... using a custom namespace
      const MY_NAMESPACE = '(previously generated unique uuid string)';
      uuidV5('hello', MY_NAMESPACE); // -> '90123e1c-7512-523e-bb28-76fab9f2f73d'
      

      ECMAScript 2015 (ES6):

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

      【讨论】:

      • 注意:这些导入对我不起作用。导入语句已更改,如 repo 中所述:const { v4: uuidv4 } = require('uuid'); 和 ES6:import { v4 as uuidv4 } from 'uuid';
      【解决方案13】:
      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 类型 4(随机)模式,因为我上次使用 Java 的 UUID 解析不兼容的 UUID 时遇到了问题

      【讨论】:

        【解决方案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】:

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

          用法:

          Guid.newGuid()

          “c6c2d12f-d76b-5739-e551-07e6de5b0807”

          Guid.empty

          “00000000-0000-0000-0000-000000000000”

          【讨论】:

          • all 答案的困扰在于,JavaScript 将 GUID 存储为 string 似乎 ok。您的回答至少使用Uint16Array 解决了更多 更有效的存储问题。 toString 函数应该使用 JavaScript object 中的二进制表示
          • 此代码生成的 UUID 要么是弱但符合 RFC (_guid),要么是强但不符合 RFC (_cryptoGuid)。前者使用 Math.random(),现在已知它是一个糟糕的 RNG。后者未能设置版本和变体字段。
          • @broofa - 你有什么建议让它变得强大 RFC 兼容?为什么 _cryptoGuid 不符合 RFC?
          • @Matt _cryptoGuid() 随机设置所有 128 位,这意味着它不设置 RFC 中描述的版本和变体字段。请参阅上面我投票率最高的答案中使用 crypto.getRandomValues() 的 uuidv4() 替代实现,以获得强大的兼容实现。
          【解决方案16】:

          以下版本是 broofa's answer 的改编版本,但更新后包含一个“真正的”随机函数,该函数在可用的情况下使用加密库,并且 Alea() 函数作为后备。

            Math.log2 = Math.log2 || function(n){ return Math.log(n) / Math.log(2); }
            Math.trueRandom = (function() {
            var crypt = window.crypto || window.msCrypto;
          
            if (crypt && crypt.getRandomValues) {
                // If we have a crypto library, use it
                var random = function(min, max) {
                    var rval = 0;
                    var range = max - min;
                    if (range < 2) {
                        return min;
                    }
          
                    var bits_needed = Math.ceil(Math.log2(range));
                    if (bits_needed > 53) {
                      throw new Exception("We cannot generate numbers larger than 53 bits.");
                    }
                    var bytes_needed = Math.ceil(bits_needed / 8);
                    var mask = Math.pow(2, bits_needed) - 1;
                    // 7776 -> (2^13 = 8192) -1 == 8191 or 0x00001111 11111111
          
                    // Create byte array and fill with N random numbers
                    var byteArray = new Uint8Array(bytes_needed);
                    crypt.getRandomValues(byteArray);
          
                    var p = (bytes_needed - 1) * 8;
                    for(var i = 0; i < bytes_needed; i++ ) {
                        rval += byteArray[i] * Math.pow(2, p);
                        p -= 8;
                    }
          
                    // Use & to apply the mask and reduce the number of recursive lookups
                    rval = rval & mask;
          
                    if (rval >= range) {
                        // Integer out of acceptable range
                        return random(min, max);
                    }
                    // Return an integer that falls within the range
                    return min + rval;
                }
                return function() {
                    var r = random(0, 1000000000) / 1000000000;
                    return r;
                };
            } else {
                // From https://web.archive.org/web/20120502223108/http://baagoe.com/en/RandomMusings/javascript/
                // Johannes Baagøe <baagoe@baagoe.com>, 2010
                function Mash() {
                    var n = 0xefc8249d;
          
                    var mash = function(data) {
                        data = data.toString();
                        for (var i = 0; i < data.length; i++) {
                            n += data.charCodeAt(i);
                            var h = 0.02519603282416938 * n;
                            n = h >>> 0;
                            h -= n;
                            h *= n;
                            n = h >>> 0;
                            h -= n;
                            n += h * 0x100000000; // 2^32
                        }
                        return (n >>> 0) * 2.3283064365386963e-10; // 2^-32
                    };
          
                    mash.version = 'Mash 0.9';
                    return mash;
                }
          
                // From http://baagoe.com/en/RandomMusings/javascript/
                function Alea() {
                    return (function(args) {
                        // Johannes Baagøe <baagoe@baagoe.com>, 2010
                        var s0 = 0;
                        var s1 = 0;
                        var s2 = 0;
                        var c = 1;
          
                        if (args.length == 0) {
                            args = [+new Date()];
                        }
                        var mash = Mash();
                        s0 = mash(' ');
                        s1 = mash(' ');
                        s2 = mash(' ');
          
                        for (var i = 0; i < args.length; i++) {
                            s0 -= mash(args[i]);
                            if (s0 < 0) {
                                s0 += 1;
                            }
                            s1 -= mash(args[i]);
                            if (s1 < 0) {
                                s1 += 1;
                            }
                            s2 -= mash(args[i]);
                            if (s2 < 0) {
                                s2 += 1;
                            }
                        }
                        mash = null;
          
                        var random = function() {
                            var t = 2091639 * s0 + c * 2.3283064365386963e-10; // 2^-32
                            s0 = s1;
                            s1 = s2;
                            return s2 = t - (c = t | 0);
                        };
                        random.uint32 = function() {
                            return random() * 0x100000000; // 2^32
                        };
                        random.fract53 = function() {
                            return random() +
                                (random() * 0x200000 | 0) * 1.1102230246251565e-16; // 2^-53
                        };
                        random.version = 'Alea 0.9';
                        random.args = args;
                        return random;
          
                    }(Array.prototype.slice.call(arguments)));
                };
                return Alea();
            }
          }());
          
          Math.guid = function() {
              return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c)    {
                var r = Math.trueRandom() * 16 | 0,
                    v = c == 'x' ? r : (r & 0x3 | 0x8);
                return v.toString(16);
            });
          };
          

          【讨论】:

            【解决方案17】:

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

            UUID.js 符合 RFC 标准的 JavaScript UUID 生成器。

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

            功能生成符合 RFC 4122 的 UUID。

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

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

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

            【讨论】:

              【解决方案18】:
                // 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('-');
              

              【讨论】:

                【解决方案19】:

                对于那些想要一个符合 RFC 4122 版本 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() );

                上述函数应该在速度和随机性之间取得不错的平衡。

                【讨论】:

                  【解决方案20】:

                  我想了解broofa's answer,所以我扩展它并添加了cmets:

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

                  【讨论】:

                  • 感谢您的详细描述!特别是 8 到 11 之间的蚕食,并附有等价物的解释非常有帮助。
                  【解决方案21】:

                  我调整了自己的 UUID/GUID 生成器,增加了一些 here

                  我正在使用 the following Kybos 随机数生成器,以便在密码学上更加可靠。

                  以下是我的脚本,其中排除了 baagoe.com 的 Mash 和 Kybos 方法。

                  //UUID/Guid Generator
                  // use: UUID.create() or UUID.createSequential()
                  // convenience:  UUID.empty, UUID.tryParse(string)
                  (function(w){
                    // From http://baagoe.com/en/RandomMusings/javascript/
                    // Johannes Baagøe <baagoe@baagoe.com>, 2010
                    //function Mash() {...};
                  
                    // From http://baagoe.com/en/RandomMusings/javascript/
                    //function Kybos() {...};
                  
                    var rnd = Kybos();
                  
                    //UUID/GUID Implementation from http://frugalcoder.us/post/2012/01/13/javascript-guid-uuid-generator.aspx
                    var UUID = {
                      "empty": "00000000-0000-0000-0000-000000000000"
                      ,"parse": function(input) {
                        var ret = input.toString().trim().toLowerCase().replace(/^[\s\r\n]+|[\{\}]|[\s\r\n]+$/g, "");
                        if ((/[a-f0-9]{8}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{12}/).test(ret))
                          return ret;
                        else
                          throw new Error("Unable to parse UUID");
                      }
                      ,"createSequential": function() {
                        var ret = new Date().valueOf().toString(16).replace("-","")
                        for (;ret.length < 12; ret = "0" + ret);
                        ret = ret.substr(ret.length-12,12); //only least significant part
                        for (;ret.length < 32;ret += Math.floor(rnd() * 0xffffffff).toString(16));
                        return [ret.substr(0,8), ret.substr(8,4), "4" + ret.substr(12,3), "89AB"[Math.floor(Math.random()*4)] + ret.substr(16,3),  ret.substr(20,12)].join("-");
                      }
                      ,"create": function() {
                        var ret = "";
                        for (;ret.length < 32;ret += Math.floor(rnd() * 0xffffffff).toString(16));
                        return [ret.substr(0,8), ret.substr(8,4), "4" + ret.substr(12,3), "89AB"[Math.floor(Math.random()*4)] + ret.substr(16,3),  ret.substr(20,12)].join("-");
                      }
                      ,"random": function() {
                        return rnd();
                      }
                      ,"tryParse": function(input) {
                        try {
                          return UUID.parse(input);
                        } catch(ex) {
                          return UUID.empty;
                        }
                      }
                    };
                    UUID["new"] = UUID.create;
                  
                    w.UUID = w.Guid = UUID;
                  }(window || this));

                  【讨论】:

                    【解决方案22】:

                    使用 Blob 的单行解决方案。

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

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


                    编辑:

                    rinogo 建议的更紧凑和通用的解决方案:

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

                    【讨论】:

                    • 或者window.URL.createObjectURL(new Blob([])).split('/').pop() 也可以做到这一点,而不必依赖 URL 长度等外部因素。
                    • 什么是“Blob”/“Blob”?
                    • @PeterMortensen Blob 是一些“原始”(二进制)数据的不透明、有效表示,以便于在 Web 上编写脚本。
                    • 嗯,这绝对行不通。要在不同的域上可靠地工作,需要将其更改为 window.URL.createObjectURL(new Blob([])).substr(-36)
                    • 这个方案有什么缺点?
                    【解决方案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()}`;
                    }
                    

                    【讨论】:

                    • 解释一下。例如,它使用了哪些 ES6 特性而以前的答案没有?请通过editing your answer 回复,而不是在 cmets 中(without "Edit:"、"Update:" 或类似的 - 答案应该看起来像是今天写的)。
                    【解决方案24】:

                    更好的方法:

                    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,
                                      ?              // generate 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}
                    

                    【讨论】:

                    • 为什么更好?
                    【解决方案25】:

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

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

                    这将返回类似2350143528-4164020887-938913176-2513998651

                    【讨论】:

                    • 顺便说一句,为什么它只生成数字而不生成字符?更不安全
                    • 你也可以像这样添加字符(字母):Array.from((window.crypto || window.msCrypto).getRandomValues(new Uint32Array(4))).map(n =&gt; n.toString(16)).join('-')
                    【解决方案26】:

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

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

                    【讨论】:

                    • 就像一个魅力。比尝试手动生成要好。非常聪明!
                    • 性能比较差,不过看情况也够了
                    • 对于最快的组合生成器,它符合 w/node-clock-seq、时间单调等。这为播种 uuid4 生成器 w/60 位 epoch70 μ 奠定了良好的基础- 单调时间的秒数,4 位 uuid 版本,48 位节点 ID 和 13 位时钟序列和 3 位 uuid 变体。 --
                      结合使用BigInt 编写ntohl 和相关转换,这与lut approach here 一起工作非常快。 --
                      如果需要,我可以提供代码。
                    • 这里是否保证包含 UUID,或者它只是当前浏览器实现都碰巧做的事情?
                    【解决方案27】:

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

                    export 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 data
                        data[6] = (data[6] & 0xf) | 0x40; /// Patch the 6th byte to reflect a version 4 UUID
                        data[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 buffer
                        return `${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 函数,可以替代使用。

                    【讨论】:

                      【解决方案28】:

                      只是另一个更具可读性的变体,只有两个突变。

                      function uuid4()
                      {
                        function hex (s, b)
                        {
                          return s +
                            (b >>> 4   ).toString (16) +  // high nibble
                            (b & 0b1111).toString (16);   // low nibble
                        }
                      
                        let r = crypto.getRandomValues (new Uint8Array (16));
                      
                        r[6] = r[6] >>> 4 | 0b01000000; // Set type 4: 0100
                        r[8] = r[8] >>> 3 | 0b10000000; // Set variant: 100
                      
                        return r.slice ( 0,  4).reduce (hex, '' ) +
                               r.slice ( 4,  6).reduce (hex, '-') +
                               r.slice ( 6,  8).reduce (hex, '-') +
                               r.slice ( 8, 10).reduce (hex, '-') +
                               r.slice (10, 16).reduce (hex, '-');
                      }
                      

                      【讨论】:

                      • 嗯,大多数 js 开发人员都是 Web 开发人员,我们不会理解位运算符的作用,因为我们在开发的大部分时间都不使用它们。实际上,我从不需要它们中的任何一个,而且自 97 年以来我就是一名 js 开发人员。因此,对于将阅读它的普通 Web 开发人员来说,您的示例代码仍然完全不可读。更不用说您仍然使用单字母变量名,这使得它更加神秘。可能阅读干净的代码,也许有帮助:amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/…
                      • @inf3rno 不要抨击他,这个线程中提出的所有解决方案都是神秘的,但考虑到问题是有一个单行,它们是正确的答案。这就是单线的神秘之处。它们无法被普通开发人员阅读,但它们节省了屏幕空间,而前面的简单评论就可以做到。结果,与使用“可读代码”相比,这种方式最终变得更具可读性。
                      • @user1529413 是的。唯一性需要一个索引。
                      • 这是我最喜欢的答案,因为它将 UUID 构建为 16 字节(128 位)值,而不是其序列化的、易于阅读的形式。删除字符串内容并设置随机 128 位的正确位非常容易,这正是 uuidv4 所需要的。您可以将其 base64 用于较短的 URL,将其传递回某个 web 程序集,将其存储在比字符串更少的内存空间中,使其成为 4096 大小的缓冲区并在其中放入 256 个 uuid,存储在浏览器数据库中,等等。好多了而不是从一开始就将所有内容都作为长的小写十六进制编码字符串。
                      【解决方案29】:

                      如果您的环境是 SharePoint,则有一个名为 SP.Guid.newGuidMSDN link 的实用程序函数会创建一个新的 GUID。此函数位于 sp.init.js 文件中。如果您重写此函数(以删除其他一些来自其他私有函数的依赖项),它看起来像这样:

                      var newGuid = function () {
                          var result = '';
                          var hexcodes = "0123456789abcdef".split("");
                      
                          for (var index = 0; index < 32; index++) {
                              var value = Math.floor(Math.random() * 16);
                      
                              switch (index) {
                              case 8:
                                  result += '-';
                                  break;
                              case 12:
                                  value = 4;
                                  result += '-';
                                  break;
                              case 16:
                                  value = value & 3 | 8;
                                  result += '-';
                                  break;
                              case 20:
                                  result += '-';
                                  break;
                              }
                              result += hexcodes[value];
                          }
                          return result;
                      };
                      

                      【讨论】:

                      • 重定向的 URL 显示 “适用于:SharePoint Foundation 2010”
                      【解决方案30】:

                      以下是在 supported browsers(Internet Explorer 11+、iOS 7+、Firefox 21+、Chrome 和 Android Chrome)上使用 crypto.getRandomValues(a) 的简单代码。

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

                      【讨论】: