协慌网

登录 贡献 社区

在 JavaScript 中创建 GUID / UUID?

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

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

答案

对于符合RFC4122版本 4 的解决方案,这种单线程 (ish)解决方案是我能想到的最紧凑的解决方案:

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

console.log(uuidv4())

更新,2015-06-02 :请注意,UUID 唯一性在很大程度上依赖于底层随机数生成器(RNG)。上面的解决方案使用Math.random()来简洁,但Math.random() 不能保证是高质量的 RNG。有关详细信息,请参阅 Adam Hyland 关于 Math.random()优秀文章 。对于更强大的解决方案,请考虑类似uuid 模块 [免责声明:我是作者],它使用更高质量的 RNG API。

更新,2015-08-26 :作为附注,这个要点描述了如何确定在达到一定的碰撞概率之前可以生成多少个 ID。例如,对于 3.26x10 15版本 4 RFC4122 UUID,您有一个百万分之一的碰撞机会。

更新,2017-06-28Chrome 开发人员在 Chrome,Firefox 和 Safari 中讨论 Math.random PRNG 质量状态的好文章 。 tl; dr - 截至 2015 年底,它 “非常好”,但不是加密质量。为了解决这个问题,这里是上述解决方案的更新版本,该解决方案使用 ES6, crypto API 和一些 JS 向导,我不能相信

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

在这方面有几次尝试。问题是:您想要实际的 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

注意 :提供的代码段不遵循 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"/>

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

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

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


这是一个小小的测试。