Как создать GUID / UUID?


4183

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

GUID / UUID должен содержать не менее 32 символов и должен оставаться в диапазоне ASCII, чтобы избежать проблем при их передаче.


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

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

Странно, что никто еще не упомянул об этом, но для полноты картины есть множество генераторов guid на npm. Я готов поспорить, что большинство из них работает и в браузере.
Джордж Мауэр

Ответы:


2339

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

Хотя можно реализовать RFU-совместимые UUID в нескольких строках 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

4122

Для решения, совместимого с 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 UUID RFC4122 версии 4 у вас есть вероятность столкновения 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 и вручную изменят переменную так, чтобы они захотели. Или они могут просто отправить вам идентификатор, который они хотят. Это также будет зависеть от того, вызовет ли пользователь, выбирающий его собственный идентификатор, уязвимости. В любом случае, если в таблицу входит идентификатор случайного числа, я, вероятно, сгенерирую его на стороне сервера, так что я знаю, что могу контролировать процесс.
Кэм Джексон

36
@DrewNoakes - UUID - это не просто строка совершенно случайных #. «4» - это версия uuid (4 = «random»). «Y» обозначает, где нужно внедрить вариант uuid (в основном, расположение полей). См. Разделы 4.1.1 и 4.1.3 ietf.org/rfc/rfc4122.txt для получения дополнительной информации.
Брооф

5
почему c== 'x'вместо c === 'x'. Потому что Jshint не удалось.
Fizer Khan

811

Мне действительно нравится, насколько чист ответ Бруфы , но, к сожалению, плохие реализации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 (), всегда увеличиваются с постоянной скоростью , независимо от системных часов, которые можно настраивать вручную или искажать с помощью программного обеспечения, такого как сетевой протокол времени.
Даниэльмб

6
@daniellmb Вы, вероятно, должны были связаться с MDN или другим, чтобы показать реальную документацию, а не polyfill;)
Martin

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

2
@Praveen Эта операция сдвигает временную метку на одну шестнадцатеричную цифру вправо и удаляет оставшуюся часть. Его цель - избавиться от только что использованной шестнадцатеричной цифры (наименее значимой) и подготовить ее к следующей итерации.
Briguy37

431

Ответ broofa довольно приятный, действительно - впечатляюще умный, действительно ... 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.

Итак, моей 6 - й итерации оптимизаций, я бью самый популярный ответ более чем на 12й , Принятый ответ более чем на на 9X , и быстро несоответствующий ответ по 2-3x . И я все еще совместим с rfc4122.

Заинтересованы в том, как? Я поставил полный источник на http://jsfiddle.net/jcward/7hyaC/3/ и на http://jsperf.com/uuid-generator-opt/4

Для объяснения давайте начнем с кода брофы:

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, и используя цикл while (вместоreplace() обратных вызовов) дает нам почти трехкратное улучшение!

Следующим шагом будет небольшой шаг на рабочем столе, но он будет иметь большое значение для мобильных устройств. Давайте сделаем меньше вызовов 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 бит одновременно, используя LUT из 256 элементов, и он показал убывающую отдачу от оптимизации. Несмотря на меньшее количество итераций, внутренняя логика усложнялась из-за увеличения обработки, и она выполняла то же самое на настольном компьютере и только на ~ 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 нуждается в криптостойкости, но это означает еще меньшую вероятность столкновения, что и составляет всю цель 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 битов clock_seq_hi_and_reserved в 01. Поскольку s [19] является символом '0' .. 'f', а не int 0x0..0xf, (s [19] и 0x3) | 0x8 не будет распределяться случайным образом - он будет давать больше «9» и меньше «b». Это имеет значение, только если вы по какой-то причине заботитесь о случайном распределении.
Джон Велонис

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

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

Если два идентификатора генерируются с более короткими интервалами и при условии, что случайный метод является действительно случайным, это приведет к созданию идентификаторов, которые с вероятностью 99,999999999999999% будут глобально уникальными (столкновение в 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, который вам затем придется преобразовать в шестнадцатеричное. Для меня это не добавляет ничего, кроме соответствия длине удостоверения личности.
Саймон Ригет

5
Перенос на MAC-адреса для уникальности на виртуальных машинах - плохая идея!
Саймон Ригет

1
Я делаю что-то вроде этого, но с ведущими символами и некоторыми черточками (например, [slug, date, random].join("_")для создания usr_1dcn27itd_hj6onj6phr. Это делает его таким образом, чтобы идентификатор также
Сеф Рид

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.
  • Чтобы различать эти два типа, я добавил параметр flag в функцию создания пар _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.
человечество

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, который является одновременно более коротким и уникальным, чем RFID-совместимые 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

От технического блога Саги Шкеди :

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 не может гарантировать уникальные ключи (см. Статью в Википедии ). Всегда есть вероятность столкновения. GUID просто предлагает достаточно большой набор ключей, чтобы свести к минимуму изменение коллизий.


8
Обратите внимание, что это не GUID в техническом смысле, потому что он не делает ничего, чтобы гарантировать уникальность. Это может иметь или не иметь значения в зависимости от вашего приложения.
Стивен Декен

2
Краткое примечание о производительности. Это решение создает 36 строк всего, чтобы получить один результат. Если производительность критична, рассмотрите возможность создания массива и объединения в соответствии с рекомендациями: tinyurl.com/y37xtx Дальнейшие исследования показывают, что это может не иметь значения, поэтому YMMV: tinyurl.com/3l7945
Брэндон Дюретт,

2
Что касается уникальности, стоит отметить, что версии 1,3 и 5 идентификаторов UUID являются детерминированными в отличие от версии 4. Если входные данные для этих генераторов uuid - идентификатор узла в v1, пространство имен и имя в v3 и v5 - уникальны (как и должно быть), то результирующие UUID будут уникальными. Во всяком случае, в теории.
Брофа

41

Вы можете использовать node-uuid ( https://github.com/kelektiv/node-uuid )

Простое, быстрое поколение RFC4122 UUIDS.

Особенности:

  • Создайте UUID RFC4122 версии 1 или версии 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 / мастер / 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 Type 4 (случайная), так как в прошлый раз у меня были проблемы с анализом несовместимых UUID с 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"

Guid.empty

"00000000-0000-0000-0000-000000000000"


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

Эти UUID, создаваемые этим кодом, являются либо слабыми, но RFC-совместимыми (_guid), либо сильными, но не RFC-совместимыми (_cryptoGuid). Первый использует Math.random (), который сейчас известен как плохой ГСЧ. Последний не может установить версию и варианты полей.
Брофа

@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 RFC-совместимый генератор UUID для 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

Я хотел понять ответ брофы, поэтому я расширил его и добавил комментарии:

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 с некоторыми дополнениями здесь .

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

Ниже мой сценарий с исключенными методами 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() );

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



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

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 сгенерированных uuids в реальной ситуации 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.


Кстати, почему он генерирует только цифры, а не символы? гораздо менее безопасно
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 - веб-разработчики, и мы не поймем, что делают побитовые операторы, потому что мы не используем их большую часть времени, когда мы разрабатываем. На самом деле я никогда не нуждался ни в одном из них, и с 1997 года являюсь разработчиком JS. Таким образом, ваш пример кода все еще не читается обычным веб-разработчиком, который его прочтет. Не говоря уже о том, что вы по-прежнему используете однобуквенные имена переменных, что делает его еще более загадочным. Вероятно, прочитайте Чистый код, возможно, это поможет: amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/…
inf3rno

@ inf3rno не ругайте его, все предложенные решения в этой ветке загадочны, но они являются правильными ответами, учитывая, что вопрос должен был быть в одном ряду. это то, что однострочники загадочны. они не могут позволить себе быть читаемыми для среднего разработчика, но они экономят пространство экрана, где подойдет простой предыдущий комментарий. И в результате получается гораздо более читабельным, чем если бы он был в «читаемом коде».
Tatsu

Случайно! = Уникально
user1529413

@ user1529413 Да. Уникальность требует индекса.
ceving

Это мой любимый ответ, потому что он строит UUID как 16-байтовое (128-битное) значение, а не его сериализованную, удобную для чтения форму. Было бы довольно просто отбросить строковый материал и просто установить правильные биты случайного 128-битного кода, что все, что нужно uuidv4. Вы можете использовать его для более коротких URL-адресов, передать его обратно какой-либо веб-сборке, сохранить его в меньшем объеме памяти, чем в виде строки, сделать его буфером размером 4096 и поместить в него 256 единиц uuid, сохранить в базе данных браузера и т. Д. Гораздо лучше чем иметь все как длинную строчную шестнадцатеричную строку с самого начала.
Джош из Карибу

8

Хорошо, используя пакет uuid , поддержка UUID версий 1, 3, 4 и 5 :

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, в котором включена функция bower, чтобы я мог легко получать обновления.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.