Як створити GUID / UUID?


4174

Я намагаюся створити унікальні глобальні ідентифікатори в JavaScript. Я не впевнений, які підпрограми доступні у всіх браузерах, наскільки "випадковим" і засіяним вбудованим генератором випадкових чисел тощо.

GUID / UUID повинен містити не менше 32 символів і повинен залишатися в діапазоні ASCII, щоб уникнути неприємностей при переході їх навколо.


13
GUID, коли вони представлені як рядки, мають принаймні 36 і не більше 38 символів і відповідають шаблону ^ \ {? [A-zA-Z0-9] {36}? \} $ І, отже, завжди є ascii.
AnthonyWJones

2
Девід Бау забезпечує набагато кращий, зручніший генератор випадкових чисел на сайті davidbau.com/archives/2010/01/30/…. Я написав дещо інший підхід до створення UUID на blogs.cozi.com/tech/2010/04/generating- uuids-in-javascript.html
Джордж В. Рейлі

Дивно, що ніхто ще не згадав про це, але для повноти, є безліч керівників генераторів в npm, я готовий зробити ставку, що більшість з них теж працює в браузері.
Джордж Мауер

Відповіді:


2338

UUID (Універсально унікальний ідентифікатор), також відомий як GUID (глобально унікальний ідентифікатор), згідно з RFC 4122 , є ідентифікаторами, призначеними для забезпечення певних гарантій унікальності.

Хоча UUID, сумісні з RFC, можна реалізувати в декількох рядках JS (наприклад, див . Відповідь broofa нижче), є кілька загальних підводних каменів:

  • Недійсний формат ідентифікатора (UUID повинні мати вигляд " xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx", де x - один із [0-9, af] M - один із [1-5], і N - [8, 9, a або b]
  • Використання неякісного джерела випадковості (наприклад, Math.random)

Таким чином, розробникам програмного коду для виробничих середовищ рекомендується використовувати жорстку, доглянуту реалізацію, таку як модуль uuid .


186
Насправді, RFC дозволяє використовувати UUID, створені з випадкових чисел. Вам просто потрібно згорнути пару біт, щоб визначити це як таке. Див. Розділ 4.4. Алгоритми створення UUID з істинно випадкових чи псевдовипадкових
Джейсон ДеФонтес,

Опираючись на все в цій нитці, я створив версію, яка вдвічі швидша за варіант "e7" нижче, крипто-сильна, і працює на всіх основних браузерах та вузлах. Тут занадто велике, щоб включити сюди, тому шукайте нову відповідь з моїм іменем 17 травня 2020 року.
Беннет Барух

node-uuid тепер став uuid (після об'єднання з останнім проектом)
Catweazle

4107

Для рішення, сумісного з 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()це НЕ гарантовано високої якості ГСЧ. Детальні відомості див. У відмінній реєстрації Адама Хайленда на Math.random () . Для більш надійного рішення розгляньте можливість використання модуля uuid , який використовує API високої якості RNG.

Оновлення, 2015-08-26 : як бічна примітка, ця суть описує, як визначити, скільки ідентифікаторів можна сформувати до досягнення певної ймовірності зіткнення. Наприклад, з 3,26x10 15 версією 4 RFC4122 UUID у вас є шанс на зіткнення 1 на мільйон.

Оновлення, 2017-06-28 : Хороша стаття розробників Chrome, яка обговорює стан якості PRNG Math.random у Chrome, Firefox та Safari. tl; dr - На кінець 2015 року це "досить добре", але не криптографічна якість. Щоб вирішити цю проблему, ось оновлена ​​версія вищезазначеного рішення, яка використовує ES6, cryptoAPI та трохи майстра 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());

Оновлення, 2020-01-06 : В роботі є пропозиція щодо стандартного uuidмодуля як частини мови JS


30
Я відповідав на питання про зіткнення stackoverflow.com/questions/6906916 / ...
Muxa

4
@marc - Якість Math.random () викликає занепокоєння. Але без детального аналізу основної реалізації, яка майже напевно варіює x-браузер, ми не можемо знати реальних шансів зіткнення. Тому для простоти я припускаю ідеальне джерело випадковості. Але, так, це може бути небезпечним припущенням, як підкреслюється питання muxa. Це також чому в node-uuid ( github.com/broofa/node-uuid ) я віддаю перевагу іншим API, які гарантують випадковість криптографічної якості над Math.random (), хоча продуктивність для цього погіршує.
брофа

144
Безумовно, відповідь на питання @ Muxa - "ні"? Ніколи не можна по-справжньому безпечно довіряти чомусь, що надійшло від клієнта. Я думаю, це залежить від того, наскільки ваші користувачі можуть підняти консоль javascript і вручну змінити змінну так, щоб вони хотіли. Або вони могли просто розмістити вам назад той ідентифікатор, який вони хочуть. Це також залежатиме від того, чи буде користувач, який вибирає власний ідентифікатор, спричинить уразливості. У будь-якому випадку, якщо це ідентифікатор випадкового числа, який збирається в таблицю, я, ймовірно, генерую його на стороні сервера, так що я знаю, що маю контроль над процесом.
Cam Jackson

36
@DrewNoakes - UUID - не просто рядок повністю випадкових #. "4" - це версія uuid (4 = "випадкова"). Позначення "y", де потрібно вбудувати варіант uuid (в основному компонування поля). Додаткову інформацію див. У розділах 4.1.1 та 4.1.3 ietf.org/rfc/rfc4122.txt .
брофа

5
чому c== 'x'замість c === 'x'. Тому що jshint не вдається.
Фізер Хан

810

Мені дуже подобається, наскільки чітка відповідь Broofa , але прикро, що погані реалізації неMath.random залишають шансів на зіткнення.

Ось аналогічне рішення, сумісне з RFC4122 версії 4, яке вирішує цю проблему шляхом зміщення перших 13 шістнадцяткових номерів шістнадцятковою частиною часової позначки та одноразового вичерпування зміщення шестнадцятковою частиною мікросекунд з моменту завантаження сторінки. Таким чином, навіть якщо вони Math.randomє одним і тим же насінням, обом клієнтам доведеться генерувати UUID рівно однакову кількість мікросекунд з моменту завантаження сторінки (якщо підтримується високопродуктивний час) І з точно такою ж мілісекундою (або 10 000+ років пізніше) до отримати той же UUID:

function generateUUID() { // Public Domain/MIT
    var d = new Date().getTime();//Timestamp
    var d2 = (performance && 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);
    });
}

console.log(generateUUID())


Ось загадка для тестування.


31
Майте на увазі, new Date().getTime()не оновлюється кожні мілісекунди. Я не впевнений, як це впливає на очікувану випадковість вашого алгоритму.
devios1

84
performance.now було б навіть краще. На відміну від Date.now, часові позначки, що повертаються performance.now(), не обмежуються роздільною здатністю в один мілісекунд. Натомість вони представляють рази як числа з плаваючою комою з точністю до мікросекунди . Також, на відміну від Date.now, значення, що повертаються показником performance.now (), завжди збільшуються з постійною швидкістю , незалежно від системних годин, які можуть бути відрегульовані вручну або перекошені програмним забезпеченням, таким як Network Time Protocol.
daniellmb

6
@daniellmb Ви, мабуть, мали би зв’язатись з MDN чи іншим, щоб показати реальну документацію, а не поліфактор;)
Martin

2
Чи можу я знати, в чому полягає користь округлення d = Math.floor(d/16);?
Правен

2
@Praveen Ця операція зміщує часову позначку на одну шістнадцяткову цифру праворуч і скидає решту. Її мета - позбутися щойно використаної нами шестигранної цифри (найменш значущої) та підготувати її до наступної ітерації.
Briguy37

431

Відповідь broofa - дуже чітка, справді - вражаюче розумна, справді ... rfc4122 сумісна, дещо читабельна та компактна. Дивовижно!

Але якщо ви дивитесь на цей регулярний вираз, ті безлічі replace()зворотних викликів, викликів toString()та Math.random()функцій (де він використовує лише 4 біти результату і витрачає решту), ви можете почати замислюватися про ефективність. Дійсно, joelpt навіть вирішив викинути RFC для загальної швидкості GUID за допомогою generateQuickGUID.

Але чи можемо ми отримати швидкість та відповідність RFC? Я кажу: ТАК! Чи можемо ми зберегти читабельність? Ну ... Не дуже, але легко, якщо ви будете слідувати далі.

По-перше, мої результати порівняно з брофою guid(прийнята відповідь) та невідповідним стандартам 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.

Тож за моєю шостою ітерацією оптимізацій я переміг найпопулярнішу відповідь понад 12X , прийняту відповідь понад 9X та швидку несумісну відповідь на 2-3X . І я все ще сумісний з rfc4122.

Цікавить, як? Я розмістив повне джерело на 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випадковими даними (за винятком примушування 2-х верхніх біт 10на специфікацію RFC), і регулярний вираз не відповідає символам -або 4знакам, тому він не повинен з ними мати справу. Дуже, дуже струнка.

Перше, що потрібно знати, це те, що виклики функцій є дорогими, як і регулярні вирази (хоча він використовує лише 1, у нього є 32 зворотні виклики, по одному на кожен матч, і в кожному з 32 зворотних зворотних дзвінків він називає Math.random () і v. toString (16)).

Першим кроком до продуктивності є усунення RegEx та його функцій зворотного виклику та використання простого циклу. Це означає , що ми повинні мати справу з -і 4символів , тоді як broofa не зробив. Також зауважте, що ми можемо використовувати індексацію String Array для збереження його архітектурної архітектури шаблонів String:

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

console.log(e1())

В основному, та сама внутрішня логіка, за винятком того, що ми перевіряємо -чи 4, і використовуємо цикл час (замість цього)replace() зворотних викликів) отримує нам майже поліпшення 3X!

Наступним кроком є ​​невеликий на робочому столі, але це робить гідну різницю в мобільному. Давайте зробимо менше викликів 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())

Я спробував e6 (), який обробляє 16 біт одночасно, все ще використовуючи 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 байт випадкових даних - це легка частина. Вся хитрість полягає в тому, щоб виразити його у String-форматі з дотриманням RFC, і це найтісніше виконання з 16-ти байтами випадкових даних, незакрученим циклом та таблицею пошуку.

Я сподіваюсь, що моя логіка правильна - дуже легко помилитися в такому виснажливому доробку. Але результати мені добре виглядають. Я сподіваюся, що вам сподобалася ця шалена їзда через оптимізацію коду!

Будьте в курсі: моя головна мета полягала в тому, щоб показати і навчити потенційні стратегії оптимізації. Інші відповіді стосуються важливих тем, таких як зіткнення та справді випадкові числа, які важливі для створення хороших UUID.


14
Цей код все ще містить пару помилок: Math.random()*0xFFFFFFFFрядки повинні бути Math.random()*0x100000000для повної випадковості, і їх >>>0слід використовувати замість того, |0щоб зберігати значення без підпису (хоча з поточним кодом, я думаю, він виходить добре, навіть якщо вони підписані). Нарешті, було б дуже корисно в ці дні використовувати їх, window.crypto.getRandomValuesякщо вони доступні, і повертатися до Math.random лише в разі крайньої необхідності. Math.random може мати менше 128 біт ентропії, і в цьому випадку це буде більш уразливим для зіткнень, ніж необхідно.
Дейв

Спираючись на все, що вже є на цій темі, я створив щось вдвічі швидше, ніж "e7", переносить усі середовища, включаючи вузол, і модернізував з Math.random () до випадковості криптовалюти. Ви можете не вважати, що uuid потребує криптовалюти, але це означає, що навіть менша ймовірність зіткнення, яка полягає у всьому суті ууїда. Занадто великий, щоб вмістити коментар, я розмістив його окремо.
Беннетт Барух

164

Ось деякий код на основі 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;
}

4
Ви повинні заздалегідь оголосити розмір масиву, а не розміщувати його динамічно під час створення GUID. var s = new Array(36);
MgSam

1
Я думаю, що в рядку є дуже незначна помилка, яка встановлює біти 6-7 годин__q____ зарезервовано до 01. Оскільки s [19] є символом '0' .. 'f', а не int 0x0..0xf, (s [19] та 0х3) | 0x8 не буде розподілятися випадковим чином - воно, як правило, виробляє більше '9 і менше' b. Це має значення лише в тому випадку, якщо ви переймаєтесь випадковим розподілом з якихось причин.
Джон Велоніс

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

Якщо ідентифікатори генеруються більше 1 мілісекунди, вони на 100% унікальні.

Якщо два ідентифікатори генеруються через більш короткі проміжки часу, і якщо припустити, що випадковий метод є справді випадковим, це призведе до отримання ідентифікаторів, які на 99,99999999999999% можуть бути унікальними у всьому світі (зіткнення в 1 з 10 ^ 15)

Ви можете збільшити це число, додавши більше цифр, але для створення 100% унікальних ідентифікаторів вам потрібно буде використовувати глобальний лічильник.

якщо вам потрібна сумісність RFC, це форматування буде передано як дійсний GUID версії 4:

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

Редагувати: наведений вище код відповідає наміру, але не букві RFC. Серед інших розбіжностей, кілька коротких випадкових цифр. (Додайте більше випадкових цифр, якщо вам це потрібно) Переваги полягають у тому, що це дуже швидко :) Ви можете перевірити дійсність свого GUID тут


4
Це не UUID, хоча?
Марко Кервіц

Ні. UUID / GUID - це 122-бітове (+ шість зарезервованих біт) числа. це може гарантувати унікальність завдяки глобальній службі лічильника, але часто вона передає час, MAC-адресу та випадковість. UUID не випадкові! Пропонований тут UID не повністю стиснутий. Ви можете стиснути його до 122-бітового цілого числа, додати 6 заздалегідь визначених бітів та зайві випадкові біти (видаліть кілька біт таймера), і в кінцевому підсумку ви отримаєте ідеально сформований UUID / GUID, який вам потім доведеться перетворити в шістнадцятковий. Мені, що насправді не додає нічого, крім відповідності довжині посвідчення особи.
Simon Rigét

5
Відпочинок на MAC-адресах для унікальності на віртуальних машинах - погана ідея!
Simon Rigét

1
Я роблю щось подібне, але з провідними персонажами та деякими тире (наприклад, [slug, date, random].join("_")щоб створити usr_1dcn27itd_hj6onj6phr. Це робить його таким, що ідентифікатор також подвоюється як поле "створене в"
Seph Reed

95

Найшвидший GUID як метод генератора рядків у форматі XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX. Це не генерує стандарт, сумісний із 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"(рядок з дев'ятьма нулями), а потім відрізаю його substr()функцією, щоб зробити його точно дев'ятьма символами (заповнюючи нулі праворуч).
  • Причина додавання рівно дев'яти нулів - це в гіршому випадку, коли Math.random()функція поверне рівно 0 або 1 (ймовірність 1/10 ^ 16 для кожної з них). Ось чому нам потрібно було додати до нього дев'ять нулів ( "0"+"000000000"або "1"+"000000000"), а потім відрізати її від другого індексу (3-го символу) довжиною у вісім символів. У решті випадків додавання нулів не завдасть шкоди результату, оскільки це все-таки скоротить його.
    Math.random().toString(16)+"000000000").substr(2,8).

Збірка:

  • GUID складається у наступному форматі XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.
  • Я розділив GUID на 4 частини, кожен фрагмент розділений на 2 типи (або формати): XXXXXXXXі -XXXX-XXXX.
  • Тепер я будувати GUID з допомогою цих 2 -х типів для збирання GUID з викликом 4 -х штук, таким чином : XXXXXXXX -XXXX-XXXX -XXXX-XXXX XXXXXXXX.
  • Щоб відрізнятись між цими двома типами, я додав параметр прапора до функції створення пари _p8(s), sпараметр вказує функції, додавати тире чи ні.
  • Врешті-решт, ми будуємо GUID із наступним ланцюжком: _p8() + _p8(true) + _p8(true) + _p8()та повертаємо його.

Посилання на цю публікацію в моєму блозі

Насолоджуйтесь! :-)


13
Ця реалізація є неправильною. Деякі символи GUID вимагають спеціального звернення (наприклад, 13-та цифра повинна бути цифрою 4).
JLRishe

67

Ось комбінація відповіді , яка проголосується вгорі , із вирішенням проблем зіткнення Chrome :

generateGUID = (typeof(window.crypto) != 'undefined' && 
                typeof(window.crypto.getRandomValues) != 'undefined') ?
    function() {
        // If we have a cryptographically secure PRNG, use that
        // /programming/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
        // /programming/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, якщо ви хочете перевірити його.


3
зауважте, що перша версія - це одне `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.
humanityANDpeace

66

Ось абсолютно невідповідна, але дуже ефективна реалізація для створення унікального ідентифікатора, схожого на 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, 10 мільйонів ітерацій кожен.

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

62

Ось рішення від 9 жовтня 2011 року з коментаря користувача jed за адресою https://gist.github.com/982883 :

UUIDv4 = function b(a){return a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,b)}

Це досягає тієї самої мети, що і відповідь , що відповідає найвищому рейтингу , але на 50+ менше байт шляхом використання примусу, рекурсії та експоненціальних позначень. Для тих, хто цікавиться, як це працює, ось анотована форма старішої версії функції:

UUIDv4 =

function b(
  a // placeholder
){
  return a // if the placeholder was passed, return
    ? ( // a random number from 0 to 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
      )
}

52

З технічного блогу sagi shkedy :

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 ). Завжди є ймовірність зіткнень. GUID просто пропонує достатньо велику всесвітню клавіш, щоб зменшити зміну зіткнень майже до нуля.


8
Зауважте, що це не GUID в технічному розумінні, оскільки він нічого не гарантує унікальності. Це може не мати значення в залежності від вашої заявки.
Стівен Декен

2
Швидка примітка про продуктивність. Це рішення створює 36 рядків, щоб отримати єдиний результат. Якщо продуктивність є критичною, подумайте про створення масиву та приєднання до нього, як рекомендує: tinyurl.com/y37xtx Подальше дослідження показує, що це може не мати значення, тому YMMV: tinyurl.com/3l7945
Brandon DuRette,

2
Що стосується унікальності, то варто зазначити, що версія 1,3 та 5 UUID є детермінованими таким чином, що версія 4 не є. Якщо входи до цих генераторів uuid - ідентифікатор вузла в v1, простір імен та ім’я в v3 та v5 - унікальні (як і належить), то отримані UUID будуть унікальними. Теоретично, все одно.
брофа

41

Ви можете використовувати node-uuid ( https://github.com/kelektiv/node-uuid )

Просте, швидке покоління UUIDS RFC4122.

Особливості:

  • Створити RFC4122 версії 1 або UUID версії 4
  • Працює в node.js та браузерах.
  • Криптографічно сильне випадкове # покоління на підтримуваних платформах.
  • Маленький слід (Хочете чогось меншого? Перевірте це! )

Встановлення за допомогою NPM:

npm install uuid

Або Використання uuid через браузер:

Завантажте файл "RAW" (uuid v1): https://raw.githubusercontent.com/kelektiv/node-uuid/master/v1.js Завантажити файл "RAW" (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'

ES6:

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

34
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 (випадкова), оскільки у мене були проблеми минулого разу при аналізі UUID Java, що не відповідають стандартам.


31

Простий модуль 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());

Використання:

Guid.newGuid ()

"c6c2d12f-d76b-5739-e551-07e6de5b0807"

Посібник.порожня

"00000000-0000-0000-0000-000000000000"


1
Що стосується всіх відповідей, це те, що JavaScript здається нормальним для зберігання файлів GUIDяк string. Ваша відповідь принаймні вирішує набагато ефективніше сховище за допомогою Uint16Array. toStringФункція повинна бути з допомогою двійкового представлення в JavaScriptobject
Себастьян

Ці UUID, створені цим кодом, або слабкі, але не сумісні з RFC (_guid), або сильні, але не відповідають RFC (_cryptoGuid). Перший використовує Math.random (), який, як відомо, поганий RNG. Останній не в змозі встановити версії та варіанти варіантів.
брофа

@broofa - Що б ти запропонував зробити його міцним та сумісним із RFC? І чому _cryptoGuid не відповідає RFC?
Метт

@Matt _cryptoGuid () встановлює всі 128 біт випадковим чином, тобто не встановлює поля версій та варіантів, як описано в RFC. Дивіться мою альтернативну реалізацію uuidv4 (), яка використовує crypto.getRandomValues ​​() у моїй голосовій відповіді вище, для сильної + сумісної реалізації.
брофа

29

Це створює UUID версії 4 (створений з псевдо випадкових чисел):

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

28

Ну, на це вже є маса відповідей, але, на жаль, у купі немає "справжньої" випадкової випадковості. Версія нижче - це адаптація відповіді 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 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);
  });
};

27

Проект JavaScript на GitHub - https://github.com/LiosK/UUID.js

UUID.js Генератор UUID, сумісний з RFC, для JavaScript.

Див. RFC 4122 http://www.ietf.org/rfc/rfc4122.txt .

Особливості Створює UUID, сумісні з RFC 4122.

Доступні UUID версії 4 (UUID від випадкових чисел) та UUID версії 1 (UUID, засновані на часі).

Об'єкт UUID дозволяє отримати різноманітний доступ до UUID, включаючи доступ до полів UUID.

Низька роздільна здатність часової мітки JavaScript компенсується випадковими числами.


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

Я хотів зрозуміти відповідь 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);
        }
    );
};

Дякую за детальний опис! Зокрема, дуже корисно гризти клітки між 8 та 11 із поясненням еквівалентів.
Єгор Литвинчук

15

Скориговані мій власний UUID / GUID генератор з деякою масовкою тут .

Я використовую наступний генератор випадкових чисел Kybos, щоб бути трохи більш криптографічним.

Нижче наведено мій сценарій з виключенням методів Mash і Kybos з baagoe.com.

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

15

Для тих, хто хоче рішення, сумісне з rfc4122 версії 4, з врахуванням швидкості (кілька дзвінків на Math.random ()):

var rand = Math.random;

function UUID() {
    var nbr, randStr = "";
    do {
        randStr += (nbr = rand()).toString(16).substr(3, 6);
    } while (randStr.length < 30);
    return (
        randStr.substr(0, 8) + "-" +
        randStr.substr(8, 4) + "-4" +
        randStr.substr(12, 3) + "-" +
        ((nbr*4|0)+8).toString(16) + // [89ab]
        randStr.substr(15, 3) + "-" +
        randStr.substr(18, 12)
    );
}

console.log( UUID() );

Вищенаведена функція повинна мати пристойний баланс між швидкістю та випадковістю.


13

Зразок ES6

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

12

Кращий спосіб:

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}

11

Я знаю, це старе питання. Просто для повноти, якщо ваше середовище SharePoint, є функція утиліти під назвою SP.Guid.newGuid( msdn посилання ), яка створює нове керівництво. Ця функція знаходиться у файлі 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;
};

11

Цей заснований на даті і додайте випадковий суфікс, щоб "забезпечити" унікальність. Добре працює для ідентифікаторів 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;
        };

11

Простий код, який використовується crypto.getRandomValues(a)в підтримуваних браузерах (IE11 +, iOS7 +, FF21 +, Chrome, Android Chrome). Уникайте використання, Math.random()оскільки це може спричинити зіткнення (наприклад, 20 зіткнень за 4000 породжених уюдів у реальній ситуації від Muxa ).

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

Примітки:

  • Оптимізовано для читання коду, а не швидкості, тому підходить для скажіння декількох сотень уйд в секунду. Створює близько 10000 uuid () в секунді в Chromium на своєму ноутбуці, використовуючи http://jsbin.com/fuwigo/1 для вимірювання продуктивності.
  • Використовує лише 8 для "y", оскільки це спрощує читабельність коду (y дозволено бути 8, 9, A або B).

11

Якщо вам просто потрібна випадкова 128-бітна рядок у не конкретному форматі, ви можете використовувати:

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

Що поверне щось подібне 2350143528-4164020887-938913176-2513998651.


BTW, чому він генерує лише числа, а не символи? набагато менш безпечний
vsync

1
Ви також можете додати символи (літери) так:Array.from((window.crypto || window.msCrypto).getRandomValues(new Uint32Array(4))).map(n => n.toString(16)).join('-')
magikMaker

11

Просто ще один читаний варіант із лише двома мутаціями.

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-розробників - це веб-розробники, і ми не зрозуміємо, що роблять оператори, що розряджаються, тому що ми не використовуємо їх у більшості випадків, коли ми розробляємо. Насправді мені ніколи не потрібна була жодна з них, і я js dev з '97 року. Тож ваш приклад код все ще повністю не читається середньому веб-розробнику, який прочитає його. Не кажучи вже про те, що ви все ще використовуєте одноіменні назви змінних, що робить його ще більш виразним. Можливо, прочитайте Чистий код, можливо, це допоможе: amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/…
inf3rno

@ inf3rno не збивайтеся з нього, всі запропоновані рішення в цій темі криптовалютні, але вони є правильними відповідями, враховуючи, що питання полягає у тому, щоб мати однотипний тип. ось те, що однолінійки криптовалютні. вони не можуть дозволити собі читати середнього розробника, але вони зберігають нерухомість на екрані, де буде зроблено простий попередній коментар. І як результат, в кінцевому підсумку це набагато читабельніше, ніж якщо б він був замість "читабельного коду".
тацу

Випадковий! = Унікальний
користувач1529413

@ user1529413 Так. Для унікальності потрібен індекс.
закінчення

Це моя улюблена відповідь, тому що вона створює UUID як 16-байтове (128 бітове) значення, а не його серіалізовану, приємну для читання форму. Тримати речі рядка було б просто неважко і просто встановити правильні біти випадкових 128 біт, що все, що має бути uuidv4. Ви можете базувати його64 для отримання коротших URL-адрес, повернути його до якоїсь веб-збірки, зберігати в меншій кількості пам’яті, ніж у вигляді рядка, зробити буфер розміром 4096 і помістити в нього 256 uuids, зберігати в db браузера тощо. Набагато краще ніж мати все як довгий шістнадцятковий кодований рядок із самого початку.
Джош з Карібу

8

Гаразд, використовуючи пакет uuid , він підтримує версії 1, 3, 4 та 5 UUID :

yarn add uuid

і потім:

const uuidv1 = require('uuid/v1');
uuidv1(); // ⇨ '45745c60-7b1a-11e8-9c9c-2d42b21b1a3e'

Ви також можете зробити це за допомогою повністю визначених параметрів:

const v1options = {
  node: [0x01, 0x23, 0x45, 0x67, 0x89, 0xab],
  clockseq: 0x1234,
  msecs: new Date('2011-11-01').getTime(),
  nsecs: 5678
};
uuidv1(v1options); // ⇨ '710b962e-041c-11e1-9234-0123456789ab'

Для отримання додаткової інформації відвідайте сторінку npm тут


6

Важливо, щоб використовувати добре перевірений код, який підтримується більш ніж 1 автором, а не збивати власні речі для цього. Це одне з місць, де ви, мабуть, хочете віддати перевагу найбільш стабільному коду, ніж найкоротшій розумній версії, яка працює в браузері X, але не враховує ідіосинкразії Y, що часто призводить до дуже важких розслідувань помилок, ніж проявляється лише випадковим чином для деяких користувачів. Особисто я використовую uuid-js на веб -сайті https://github.com/aurigadl/uuid-js, який надає активацію, щоб я міг легко отримувати оновлення.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.