Питання Створити GUID / UUID в JavaScript?


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

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


3219


походження


Ідентифікатори GUID, коли НЕ repesented як рядки принаймні 36 і не більше 38 символів в довжину і відповідати шаблон \ {? [A-Za-Z0-9] {36}? \} $ І, отже, завжди ASCII. - AnthonyWJones
Девід Бау надає набагато краще генератор випадкових чисел на davidbau.com/archives/2010/01/30/... Я написав трохи інший підхід до створення UUID на blogs.cozi.com/tech/2010/04/generating-uuids-in-javascript.html - George V. Reilly
jsben.ch/#/Lbxoe - ось тест з різними функціями знизу - EscapeNetscape
uuid-random використовує хороший PRNG і дуже швидко. - jchook
Пізніше (дуже пізно!) На вечірку тут, але @AnthonyWJones не слід читати ваш регулярний вираз: ^ \ {? [A-fA-F0-9] {36}? \) $ - noonand


Відповіді:


На це було кілька спроб. Питання полягає в тому, чи потрібно отримати дійсні GUID, або просто випадкові числа дивись як GUID? Це досить легко для генерації випадкових чисел.

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

Однак зауважте, що такі значення не справжні GUID.

У Javascript неможливо створити дійсні ідентифікатори GUID, оскільки вони залежать від властивостей локального комп'ютера, який веб-переглядачі не піддаються. Вам потрібно буде використовувати спеціальні послуги OS, такі як ActiveX: http://p2p.wrox.com/topicindex/20339.htm

Редагувати: не правильно - RFC4122 дозволяє випадкові ("версії 4") GUID. Див. Інші відповіді на специфіку.

Примітка: наданий фрагмент коду не відповідає RFC4122, який вимагає, щоб версія (4) повинен бути інтегрований у сгенерований вихідний рядок. Не використовуйте цю відповідь якщо вам потрібні сумісні GUID.

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

var uuid = guid();

Демо:

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

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

document.getElementById('jsGenId').addEventListener('click', function() {
  document.getElementById('jsIdResult').value = guid();
})
input { font-family: monospace; }
<button id="jsGenId" type="button">Generate GUID</button>
<br>
<input id="jsIdResult" type="text" placeholder="Results will be placed here..." readonly size="40"/>


1904



Фактично RFC дозволяє використовувати UUID, створені з випадкових чисел. Вам просто доведеться покрутити пару бітів, щоб визначити його як таке. Див. Розділ 4.4. Алгоритми створення UUID з справді випадкових або псевдо-випадкових чисел: rfc-archive.org/getrfc.php?rfc=4122 - Jason DeFontes
Чи може хтось пояснити цей код мені? Схоже, що функція S4 намагається отримати випадкове значення шестигранного числа між 0x10000 та 0x20000, а потім виводить останні 4 цифри. Але чому побітовий або з 0? Хіба це не бути носом? Крім того, 0x10000 до 0x20000 просто рубати, щоб уникнути необхідності мати справу з провідними 0? - Cory
У Chrome цей код не завжди генерує правильний розмір GUID. Довжина варіює від 35 до 36 - cdeutsch
Як може така очевидно неправильна відповідь отримати так багато варіантів? Навіть код неправильний, так як немає 4 у правильному положенні. en.wikipedia.org/wiki/Globally_unique_identifier - Dennis Krøger
Ця відповідь була порушена в редакції 5, коли "1 + ...." та "підрядка (1)" були вилучені. Ці біти гарантували стійку довжину. - Segfault


Для а RFC4122 версія, сумісна з рішенням, це одношарове (іш) рішення є найбільш компактним я міг придумати .:

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. Див. Адам Хайленд відмінний запис на Math.random () для подробиць. Для більш надійного рішення розглянемо щось подібне модуль uuid[Застереження: Я автор], який використовує більш якісні API RNG, де це доступно.

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

Оновлення, 2017-06-28: A хороша стаття від розробників Chrome обговорюючи стан Math.random PRNG якості в Chrome, Firefox і Safari. tl; dr - На кінець 2015 року це "досить добре", але не криптографічна якість. Щоб вирішити цю проблему, ось оновлена ​​версія вищезгаданого рішення, що використовує ES6, crypto API, і трохи JS wizardy Я не можу взяти на себе кредит:

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


3149



Чи безпечно використовувати цей код для генерованих унікальних ідентифікаторів клієнта, а потім використовувати ці ідентифікатори як основні ключі для збереження об'єктів на сервері? - Muxa
... (продовження) Коефіцієнт збігу двох ідентифікаторів, сформованих цією функцією, буквально астрономічно малий. Усі, крім 6 з 128 бітів ідентифікатора, створюються випадковим чином, що означає, що для будь-яких двох ідентифікаторів існує 1 з 2 ^ 122 (або 5.3 × 10 ^ ^ 36) шанс, що вони зіткнуться. - broofa
Я опублікував питання про зіткнення stackoverflow.com/questions/6906916/... - Muxa
Звичайно, відповідь на запитання @ Muxa є "ні"? Ніколи не справді надійно довіряти те, що прийшло від клієнта. Я думаю, це залежить від того, наскільки імовірно ваші користувачі мають виховати консоль javascript, і вручну замінити змінну так, щоб вони хотіли. Або вони можуть просто відправити ідентифікатор, який вони хочуть. Це також буде залежати від того, чи буде користувач, який вибере свій ідентифікатор, викликати вразливі місця. Так чи інакше, якщо це ідентифікатор випадкових чисел, який йде в таблицю, я, мабуть, генерує його на стороні сервера, так що я знаю, що я контролюю цей процес. - Cam Jackson
@DrewNoakes - UUID - це не просто рядок повністю випадкових #. "4" - версія uuid (4 = "випадковий"). Знак "y" позначає, де має бути вставлений варіант uuid (макет поля, в основному). Див. Розділи 4.1.1 та 4.1.3 ietf.org/rfc/rfc4122.txt для отримання додаткової інформації. - broofa


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

Ось схоже RFC4122 сумісний з версією 4 рішення, яке вирішує цю проблему шляхом зсуву перших 13 hex-чисел шістнадцятковою частиною мітки часу. Таким чином, навіть якщо Math.randomна тому ж насіннику, обидва клієнти повинні були генерувати UUID за тієї ж мілісекунди (або через 10 000+ років), щоб отримати такий же 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);
    });
}


Ось скрипка для тестування.


672



Мати на увазі, new Date().getTime() не оновлюється кожні мілісекунди. Я не знаю, як це впливає на очікувану випадковість вашого алгоритму. - devios1
Я думаю, що це найкращі відповіді просто тому, що він використовує дату в її покоління. Однак якщо у вас є сучасна стек для браузерів, я рекомендую Date.now() до new Date().getTime() - Fresheyeball
performance.now було б ще краще. На відміну від Date.now, позначки часу повертаються performance.now() не обмежуються роздільною здатністю 1 мілісекунду. Замість цього вони представляють час як числа з плаваючою точкою до мікросекундна точність. Також на відміну від Date.now, значення, що повертаються performance.now () завжди зростає з постійною швидкістю, незалежно від системних годин, які можуть бути налаштовані вручну або перекошені програмним забезпеченням, таким як протокол мережевого часу. - daniellmb
@daniellmb Ви, мабуть, повинні були зв'язати з MDN чи іншою, щоб показати справжню документацію, а не поліфайл;) - Martin
FYI, за нижній колонтитул сайту, всі внески користувача на сайт доступні під ліцензією cc by-sa 3.0. - Xiong Chiamiov


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

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

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

Але спочатку мої результати, в порівнянні з брофами, 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.

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

Цікаво, як? Я поставив повне джерело http://jsfiddle.net/jcward/7hyaC/3/ і далі http://jsperf.com/uuid-generator-opt/4

Для пояснення давайте почнемо з коду Блофа:

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

Так воно заміняє x з будь-якою довільною шістнадцяткою y з випадковими даними (за винятком змушуючи 2 верхніх бітів до 10 за специфікацією RFC), а регулярний вираз не відповідає - або 4 персонажів, тому йому не доводиться мати справу з ними. Дуже, дуже гладко.

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

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

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

В основному, та сама внутрішня логіка, за винятком ми перевіряємо - або 4, і використовуючи цикл while (замість replace() зворотні виклики) приносить нам майже 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
}

Це заощаджує нас на 10-30% залежно від платформи. Непогано. Але наступний великий крок позбудеться функції calls toString разом із класифікацією оптимізації - пошуковою таблицею. Простий 16-елементний таблиця пошуку виконає завдання toString (16) за набагато менше часу:

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

Наступна оптимізація - ще одна класика. Оскільки ми обробляємо лише 4-бітні вихідні дані в кожній ітерації циклу, давайте скоротимо кількість циклів наполовину і обробляємо 8-бітову кожну ітерацію. Це складно, оскільки ми все ще повинні обробляти положення біт, сумісних із RFC, але це не так вже й важко. Потім нам доведеться створити великий таблицю пошуку (16x16 або 256) для зберігання 0x00 - 0xff, і ми будуємо його лише один раз за межами функції e5 ().

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

Я спробував 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];
}

Модалізований: http://jcward.com/UUID.js - UUID.generate()

Найцікавіше, що створення 16 байтів випадкових даних є легкою частиною. Весь трюк виражає його у форматі String з відповідністю RFC, і це найбільш тісно виконано з 16 байтами випадкових даних, розгорнутою циклом та таблицею пошуку.

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

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


308



jsperf.com дозволить вам знімати дані та побачити результати в браузерах та на пристроях. - fearphage
Привіт, @Чад, хороші запитання. k може бути переміщено назовні, але це не покращило показники вище та призводить до масштабності. І побудова масиву і приєднання до повернення незвичайно вбиває продуктивність. Але знову ж таки, не соромтеся експериментувати! - Jeff Ward
Мені цікаво подивитися, як порівнює вузол-uuid.js. У своїй роботі я почав займатися більшою частиною роботи, деякі з яких залишаються. Але я з тих пір відступився від цього, вважаючи за краще мати більш читаний / обслуговуючий код. Причиною є те, що uuid perf просто не є проблемою в реальному світі. Uidid, як правило, створюються в поєднанні з значно повільними операціями (наприклад, створення запиту мережі, створення та збереження об'єкта моделі), де бридіння декількох мікросекунд від речей просто не має значення. - broofa
Цей код ще містить кілька помилок: Math.random()*0xFFFFFFFF лінії повинні бути Math.random()*0x100000000 для повної випадковості, і >>>0 слід використовувати замість |0 щоб зберегти значення unsigned (хоча з поточним кодом я думаю, що він відійде добре, навіть якщо вони підписані). Нарешті, це буде дуже хороша ідея в ці дні використовувати window.crypto.getRandomValues якщо є, і повернення до Math.random лише в разі необхідності. Math.random може мати менше ніж 128 біт ентропії, і в цьому випадку це буде більш вразливим до зіткнень, ніж це необхідно. - Dave
Застосував всі поради @Dave і опублікував дуже акуратний ES6 / Бабель джерело тут: codepen.io/avesus/pen/wgQmaV?editors=0012 - Brian Haak


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

138



Це не дає тире, необхідного для c # для його аналізу на System.Guid. Він виглядає таким чином: B42A153F1D9A4F92990392C11DD684D2, коли він повинен виглядати як: B42A153F-1D9A-4F92-9903-92C11DD684D2 - Levitikon
ABNF з специфікації включає символи "-", тому я оновлювався для сумісності. - Kevin Hakanson
Я особисто ненавиджу тире, але кожному своїм. Ось чому ми програмісти! - devios1
Ви повинні оголосити розмір масиву заздалегідь, а не масштабувати його динамічно, коли ви створюєте GUID. var s = new Array(36); - MgSam
@ Levitikon .NET's Guid.Parse() слід проаналізувати B42A153F1D9A4F92990392C11DD684D2в Guid просто чудово. Не потрібно мати дефіси. - JLRishe


Найбільш швидкий 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 (slavik@meltser.info).
 * @link http://slavik.meltser.info/?p=142
 */
function guid() {
    function _p8(s) {
        var p = (Math.random().toString(16)+"000000000").substr(2,8);
        return s ? "-" + p.substr(0,4) + "-" + p.substr(4,4) : p ;
    }
    return _p8() + _p8(true) + _p8(true) + _p8();
}

Щоб протестувати продуктивність, ви можете запустити цей код:

console.time('t'); 
for (var i = 0; i < 10000000; i++) { 
    guid(); 
};
console.timeEnd('t');

Я впевнений, що більшість із вас зрозуміє, що я там робив, але, можливо, є принаймні одна людина, яка потребує пояснення:

Алгоритм:

  • The 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 за допомогою цих двох типів, щоб зібрати GUID з викликом 4 штук, як показано нижче: XXXXXXXX  -XXXX-XXXX  -XXXX-XXXX  XXXXXXXX.
  • Щоб відрізнятись між цими двома типами, я додав параметр прапорця до функції створення пари _p8(s), the s Параметр задає функцію додавання дефісів чи ні.
  • Зрештою ми будуємо GUID з наступним ланцюгом: _p8() + _p8(true) + _p8(true) + _p8(), і повернути його.

Посилання на цей пост в моєму блозі

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


79



Ця реалізація не відповідає дійсності. Деякі символи GUID вимагають спеціального режиму (наприклад, 13-й номер повинен бути номером 4). - JLRishe
@ JLRіше, ви маєте рацію, він не відповідає стандартам RFC4122. Але це як і раніше випадкова рядок, яка виглядає як GUID. Ура :-) - Slavik Meltser
Красива робота, але класичні методи оптимізації роблять це 6x швидше (у моєму браузері) - див моя відповідь - Jeff Ward


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

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

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

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

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


69



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


Ось комбінація Топ голосував відповідь, з можливістю обходу Зіткнення Chrome:

generateGUID = (typeof(window.crypto) != 'undefined' && 
                typeof(window.crypto.getRandomValues) != 'undefined') ?
    function() {
        // If we have a cryptographically secure PRNG, use that
        // https://stackoverflow.com/questions/6906916/collisions-when-generating-uuids-in-javascript
        var buf = new Uint16Array(8);
        window.crypto.getRandomValues(buf);
        var S4 = function(num) {
            var ret = num.toString(16);
            while(ret.length < 4){
                ret = "0"+ret;
            }
            return ret;
        };
        return (S4(buf[0])+S4(buf[1])+"-"+S4(buf[2])+"-"+S4(buf[3])+"-"+S4(buf[4])+"-"+S4(buf[5])+S4(buf[6])+S4(buf[7]));
    }

    :

    function() {
        // Otherwise, just use Math.random
        // https://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
            var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
            return v.toString(16);
        });
    };

На jsbin якщо ви хочете перевірити його.


57



Я вважаю, що в IE фактично це window.msCrypto, а не window.crypto. Може бути приємно перевірити обидва. Побачити msdn.microsoft.com/en-us/library/ie/dn265046(v=vs.85).aspx - herbrandson
Зверніть увагу, що перша версія, одна `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


Ось рішення від 9 жовтня 2011 року від коментаря користувача джед при 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



У TypeScript використовуйте це: export const UUID = function b (a: number): string { return a ? (a^Math.random()*16>>a/4).toString(16) : (''+[1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, b) }; - RyanNerd


Ось повна несумісна, але дуже ефективна реалізація для створення унікального ідентифікатора, що нагадує GUID-безпечний ASCII.

function generateQuickGuid() {
    return Math.random().toString(36).substring(2, 15) +
        Math.random().toString(36).substring(2, 15);
}

Створює 26 [a-z0-9] символів, даючи UID, який є як коротшим, так і більш унікальним, ніж GUID, сумісні з RFC. Дефіси можна додати тривіально, якщо це важливо для людського читання.

Ось приклади використання та терміни для цієї функції та декілька інших відповідей на це питання. Час було виконано під керуванням 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');

52