Создать GUID / UUID в JavaScript?
Я пытаюсь создать глобально уникальные идентификаторы в JavaScript. Я не уверен, какие подпрограммы доступны во всех браузерах, насколько "случайным" и затравленным является встроенный генератор случайных чисел и т. Д.
GUID / UUID должен быть не менее 32 символов и должен оставаться в диапазоне ASCII, чтобы избежать проблем при их передаче.
83 ответа
UUID (универсальный уникальный идентификатор), также известный как GUID (глобальный уникальный идентификатор), согласно RFC 4122, являются идентификаторами с определенной гарантией уникальности.
Лучший способ их генерирования - следовать инструкциям по реализации в указанном RFC, использовать одну из многих проверенных реализаций с открытым исходным кодом или использовать встроенную реализацию для языков, в которых она есть.
Некоторые примеры инструментов с открытым исходным кодом для работы с UUID, для некоторых популярных языков программирования перечислены здесь.
JavaScript
PHP
Идти
Рубин
- Часть стандартной библиотеки
питон
Обратите внимание, что просто случайная генерация идентификаторов побайтово или посимвольно не даст вам таких же гарантий, как соответствующая реализация. Кроме того, очень важно, что системы, работающие с совместимыми UUID, могут не принимать случайно сгенерированные, и многие валидаторы с открытым исходным кодом фактически проверяют правильность структуры.
UUID должен иметь этот формат:
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
Где позиции M и N могут иметь только определенные значения. В настоящее время единственными допустимыми значениями для M являются 1, 2, 3, 4 и 5, поэтому случайная генерация этой позиции сделает большинство результатов неприемлемыми.
Для решения, совместимого с 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: В качестве дополнительного примечания в этой части описывается, как определить, сколько идентификаторов может быть сгенерировано до достижения определенной вероятности столкновения. Например, при использовании UUID RFC4122 версии 3,26x10 15 версии 4 вероятность столкновения составляет 1 на миллион.
Обновление, 2017-06-28: Хорошая статья от разработчиков Chrome, в которой обсуждается состояние качества PRNG в Math.random в Chrome, Firefox и Safari. tl;dr - По состоянию на конец 2015 года это "довольно хорошо", но не криптографическое качество. Чтобы решить эту проблему, вот обновленная версия вышеупомянутого решения, которое использует ES6, crypto
API и немного JS-волшебства, за который я не могу взять кредит:
function uuidv4() {
return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
)
}
console.log(uuidv4());
Мне действительно нравится, насколько чист ответ Бруфы, но, к сожалению, плохие реализации Math.random
оставь шанс на столкновение.
Вот похожее решение, соответствующее RFC4122 версии 4, которое решает эту проблему путем смещения первых 13 шестнадцатеричных чисел на шестнадцатеричную часть временной метки. Таким образом, даже если Math.random
на одном и том же начальном этапе оба клиента должны будут сгенерировать UUID в одну и ту же миллисекунду (или более 10000 лет спустя), чтобы получить тот же 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);
});
}
Ответ broofa довольно приятный, действительно - впечатляюще умный, действительно... rfc4122-совместимый, несколько читаемый и компактный. Потрясающие!
Но если вы посмотрите на это регулярное выражение, те многие replace()
обратные вызовы, toString()
и Math.random()
вызовы функций (когда он использует только 4 бита результата и тратит впустую все остальное), вы можете начать задумываться о производительности. Действительно, joelpt даже решил отказаться от RFC для общей скорости GUID с generateQuickGUID
,
Но можем ли мы получить скорость и соответствие RFC? Я говорю да! Можем ли мы сохранить читабельность? Ну... Не совсем, но это легко, если вы будете следовать.
Но сначала мои результаты, по сравнению с бройфой,guid
(принятый ответ) и не соответствующий RFCgenerateQuickGuid
:
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.
Таким образом, благодаря шестой итерации оптимизаций я побил самый популярный ответ более чем в 12 раз, принятый ответ более чем в 9 раз, а быстрый несоответствующий ответ - в 2-3 раза. И я все еще совместим с 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% в зависимости от платформы. Неплохо. Но следующий большой шаг избавляет от вызовов функции 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 бит одновременно, используя 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];
}
Модулированный: http://jcward.com/UUID.js - UUID.generate()
Самое смешное, что генерация 16 байтов случайных данных - самая простая часть. Весь трюк состоит в том, чтобы выразить его в формате String с соблюдением требований RFC, и он наиболее тщательно выполнен с 16 байтами случайных данных, развернутым циклом и таблицей поиска.
Я надеюсь, что моя логика верна - очень легко ошибиться в этом утомительном занятии. Но результаты выглядят хорошо для меня. Я надеюсь, вам понравилась эта безумная поездка благодаря оптимизации кода
Имейте в виду: моей главной целью было показать и научить потенциальных стратегий оптимизации. Другие ответы охватывают важные темы, такие как столкновения и действительно случайные числа, которые важны для создания хороших UUID.
var uniqueId = Math.random().toString(36).substring(2)
+ (new Date()).getTime().toString(36);
Если идентификаторы генерируются с интервалом более 1 миллисекунды, они уникальны на 100%.
Если два идентификатора генерируются с более короткими интервалами и при условии, что случайный метод является действительно случайным, это приведет к созданию идентификаторов, которые с вероятностью 99,999999999999999% будут глобально уникальными (столкновение в 1 из 10^15)
Вы можете увеличить это число, добавив больше цифр, но для создания 100% уникальных идентификаторов вам нужно будет использовать глобальный счетчик.
document.getElementById("unique").innerHTML =
Math.random().toString(36).substring(2) + (new Date()).getTime().toString(36);
<div id="unique">
</div>
Вот некоторый код, основанный на RFC 4122, раздел 4.4 (Алгоритмы для создания UUID из действительно случайного или псевдослучайного числа).
function createUUID() {
// http://www.ietf.org/rfc/rfc4122.txt
var s = [];
var hexDigits = "0123456789abcdef";
for (var i = 0; i < 36; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
}
s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
s[8] = s[13] = s[18] = s[23] = "-";
var uuid = s.join("");
return uuid;
}
Самый быстрый GUID, как метод строкового генератора в формате 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');
Я уверен, что большинство из вас поймет, что я там делал, но, возможно, есть хотя бы один человек, которому понадобится объяснение:
Алгоритм:
-
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()
и вернуть его.
Ссылка на этот пост в моем блоге
Наслаждайтесь! :-)
Вот совершенно несовместимая, но очень производительная реализация для генерации 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');
Из технического блога Саги Шкеди:
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 просто предлагает достаточно большой набор ключей, чтобы уменьшить количество столкновений почти до нуля.
Вот комбинация ответа с наибольшим количеством голосов и обходного пути для столкновений Chrome:
generateGUID = (typeof(window.crypto) != 'undefined' &&
typeof(window.crypto.getRandomValues) != 'undefined') ?
function() {
// If we have a cryptographically secure PRNG, use that
// https://stackru.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://stackru.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, если вы хотите проверить это.
Вот решение от 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
)
}
Вы можете использовать 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/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();
Однострочное решение с использованием Blobs.
window.URL.createObjectURL(new Blob([])).substring(31);
Значение в конце (31) зависит от длины URL-адреса.
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 (случайная), так как в прошлый раз у меня были проблемы с анализом несовместимых uuids с UUID в Java.
Это создает 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
Простой модуль 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"
Начиная с Node.js v15.6.0, есть встроенная функция crypto.randomUUID() .
import * as crypto from "crypto";
const uuid = crypto.randomUUID();
В браузере crypto.randomUUID()
в настоящее время поддерживается только в Chromium 92+.
Веб-сервис будет полезен.
Быстрый Google нашел: http://www.hoskinson.net/GuidGenerator/
Не могу поручиться за эту реализацию, но SOMEONE должен опубликовать добросовестный генератор GUID.
С помощью такого веб-сервиса вы могли бы разработать веб-интерфейс REST, который использует веб-сервис GUID и передает его через AJAX в javascript в браузере.
Ну, у этого уже есть куча ответов, но, к сожалению, в этой группе нет "истинного" случайного числа. Приведенная ниже версия является адаптацией ответа 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 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);
});
};
Проект 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 компенсируется случайными числами.
// 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('-');
Из старой доброй википедии есть ссылка на реализацию UUID на JavaScript.
Это выглядит довольно элегантно, и, возможно, его можно улучшить, добавив хеш IP-адреса клиента. Возможно, этот хеш может быть вставлен в серверную часть html-документа для использования клиентским JavaScript.
ОБНОВЛЕНИЕ: оригинальный сайт был случайным образом, вот обновленная версия
Для тех, кто хочет rfc4122 версии 4 совместимого решения с соображениями скорости (несколько обращений к Math.random()):
function UUID() {
var nbr, randStr = "";
do {
randStr += (nbr = Math.random()).toString(16).substr(2);
} 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)
].join("");
}
Вышеупомянутая функция должна иметь приличный баланс между скоростью и случайностью.
Я хотел понять ответ брофы, поэтому я расширил его и добавил комментарии:
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);
}
);
};
Родной URL.createObjectURL
генерирует uuid. Вы можете воспользоваться этим.
function uuid() {
const url = URL.createObjectURL(new Blob())
const [id] = url.toString().split('/').reverse()
URL.revokeObjectURL(url)
return id
}
Образец ES6
const guid=()=> {
const s4=()=> Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
return `${s4() + s4()}-${s4()}-${s4()}-${s4()}-${s4() + s4() + s4()}`;
}
Настроил мой собственный генератор 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));
Я не мог найти ответ, который использует один 16-октетный TypedArray
и DataView
, поэтому я думаю, что следующее решение для генерации UUID версии 4 для RFC будет здесь само по себе:
function uuid4() {
const ho = (n, p) => n.toString(16).padStart(p, 0); /// Return the hexadecimal text representation of number `n`, padded with zeroes to be of length `p`
const view = new DataView(new ArrayBuffer(16)); /// Create a view backed by a 16-byte buffer
crypto.getRandomValues(new Uint8Array(view.buffer)); /// Fill the buffer with random data
view.setUint8(6, (view.getUint8(6) & 0xf) | 0x40); /// Patch the 6th byte to reflect a version 4 UUID
view.setUint8(8, (view.getUint8(8) & 0x3f) | 0x80); /// Patch the 8th byte to reflect a variant 1 UUID (version 4 UUIDs are)
return `${ho(view.getUint32(0), 8)}-${ho(view.getUint16(4), 4)}-${ho(view.getUint16(6), 4)}-${ho(view.getUint16(8), 4)}-${ho(view.getUint32(10), 8)}${ho(view.getUint16(14), 4)}`; /// Compile the canonical textual form from the array data
}
Я предпочитаю это, потому что он опирается только на функции, доступные для стандартной платформы ECMAScript.
Если вам просто нужна случайная 128-битная строка без определенного формата, вы можете использовать:
function uuid() {
return crypto.getRandomValues(new Uint32Array(4)).join('-');
}
Который вернет что-то вроде 2350143528-4164020887-938913176-2513998651
,
Лучший способ:
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}