Я пытаюсь создать глобально уникальные идентификаторы в JavaScript. Я не уверен, какие подпрограммы доступны во всех браузерах, насколько «случайным» и «засеянным» является встроенный генератор случайных чисел и т. д.
GUID / UUID должен состоять как минимум из 32 символов и оставаться в диапазоне ASCII, чтобы избежать проблем при их передаче.
Дэвид Бау предоставляет гораздо лучший генератор случайных чисел с возможностью рассева в davidbau.com/archives/2010/01/30/…. Я написал немного другой подход к генерации UUID в blogs.cozi.com/tech/2010/04/generating-uuids-in-javascript.h tml.
Странно, что никто еще не упомянул об этом, но для полноты картины существует множество генераторы руководств на npm, я готов поспорить, что большинство из них также работают в браузере.
Если кому-то нужны дополнительные опции, такие как разные версии поддержки uuid и нестандартных guid, службы генерации uuid на основе REST, такие как [фунгенераторы.com/api/uuid], также являются привлекательным вариантом.
Примерно 12 лет спустя с классами BigInt и ES6 могут быть применены другие методы, обеспечивающие скорость 500 000 uuid / sec. См. Ссылку



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


UUID (универсальный уникальный идентификатор), также известный как GUID (глобальный уникальный идентификатор), согласно RFC 4122, представляют собой идентификаторы, предназначенные для обеспечения определенных гарантий уникальности.
Хотя можно реализовать совместимые с RFC UUID в нескольких строках кода JavaScript (например, см. @ broofa ответ ниже), существует несколько распространенных ошибок:
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx», где x - одно из [0-9, a-f], M - одно из [1-5], а N - [8, 9, a или b]]Math.random)Таким образом, разработчикам, пишущим код для производственных сред, рекомендуется использовать строгую, хорошо поддерживаемую реализацию, такую как модуль uuid.
Фактически, RFC допускает UUID, которые создаются из случайных чисел. Вам просто нужно повернуть пару битов, чтобы идентифицировать его как таковой. См. Раздел 4.4. Алгоритмы создания UUID из действительно случайных или псевдослучайных чисел: rfc-archive.org/getrfc.php?rfc=4122
Это не должен быть принятым ответом. На самом деле это не отвечает на вопрос - вместо этого поощряется импорт 25 000 строк кода для чего-то, что вы можете сделать с помощью одной строки кода в любом современном браузере.
@AbhiBeckert, ответ с 2008 года, и для проектов node.js может быть правильным выбрать зависимость больше, чем размер проекта
@Phil, это «очень активный вопрос», что означает, что на него должен быть дан отличный ответ с зеленой галочкой. К сожалению, это не так. В этом ответе нет ничего неправильного или неправильного (если бы он был, я бы отредактировал ответ), но ниже существует другой, гораздо лучший ответ, и я думаю, он должен быть в верхней части списка. Также вопрос конкретно касается javascript в браузере, а не node.js.
От технический блог саги шкеди:
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 просто предлагает достаточно большой набор ключей, чтобы уменьшить изменение коллизий почти до нуля.
Обратите внимание, что это не GUID в техническом смысле, потому что он не гарантирует уникальности. Это может иметь значение, а может и не иметь значения, в зависимости от вашего приложения.
Небольшая заметка о производительности. Это решение создает всего 36 строк для получения единого результата. Если производительность критична, рассмотрите возможность создания массива и объединения в соответствии с рекомендациями: tinyurl.com/y37xtx Дальнейшие исследования показывают, что это может не иметь значения, поэтому YMMV: tinyurl.com/3l7945
Что касается уникальности, стоит отметить, что UUID версий 1,3 и 5 являются детерминированными, в отличие от версии 4. Если входные данные для этих генераторов uuid - идентификатор узла в v1, пространство имен и имя в v3 и v5 - уникальны (как и должно быть), то результирующие UUID будут уникальными. Во всяком случае теоретически.
Вот код, основанный на 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. var s = new Array(36);
Я думаю, что есть очень незначительная ошибка в строке, которая устанавливает биты 6-7 clock_seq_hi_and_reserved в 01. Поскольку s [19] - это символ '0' .. 'f', а не int 0x0..0xf, (s [19] & 0x3) | 0x8 не будет распределяться случайным образом - он будет давать больше девяток и меньше b. Это имеет значение только в том случае, если вы по какой-то причине заботитесь о случайном распределении.
Это создает 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
Для решения, совместимого с 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.info(uuidv4());Обновление, 2015-06-02: имейте в виду, что уникальность UUID в значительной степени зависит от основного генератора случайных чисел (RNG). В приведенном выше решении для краткости используется Math.random(), однако Math.random() - это нет, гарантирующий высокое качество RNG. См. Подробности в отличная рецензия на Math.random () Адама Хайланда. Для более надежного решения рассмотрите возможность использования модуль uuid, который использует более качественные API-интерфейсы RNG.
Обновление, 2015-08-26: В качестве примечания, этот суть описывает, как определить, сколько идентификаторов может быть сгенерировано до достижения определенной вероятности столкновения. Например, с UUID 3.26x1015 версии 4 RFC4122 вероятность коллизии составляет 1 на миллион.
Обновление, 2017-06-28: хорошая статья от разработчиков Chrome, обсуждающий состояние качества Math.random PRNG в Chrome, Firefox и Safari. tl; dr - По состоянию на конец 2015 года «неплохо», но не по криптографическому качеству. Чтобы решить эту проблему, вот обновленная версия вышеупомянутого решения, которое использует ES6, crypto API и немного волшебства JavaScript, за которое я не могу поверить:
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.info(uuidv4());Обновление, 2020-01-06: есть предложение в работе для стандартного модуля uuid как часть языка JavaScript.
Конечно, ответ на вопрос @Muxa - «нет»? Никогда не бывает по-настоящему безопасно доверять тому, что исходит от клиента. Я предполагаю, что это зависит от того, насколько вероятно, что ваши пользователи вызовут консоль javascript и вручную изменят переменную так, чтобы она была им нужна. Или они могут просто отправить вам POST идентификатор, который им нужен. Это также будет зависеть от того, вызовет ли пользователь, выбирающий собственный идентификатор, уязвимости. В любом случае, если это случайный числовой идентификатор, который попадает в таблицу, я, вероятно, сгенерирую его на стороне сервера, чтобы знать, что я контролирую процесс.
@DrewNoakes - UUID - это не просто строка полностью случайных #. «4» - это версия uuid (4 = «случайный»). «Y» отмечает, где должен быть встроен вариант uuid (в основном, макет поля). См. Разделы 4.1.1 и 4.1.3 документа ietf.org/rfc/rfc4122.txt для получения дополнительной информации.
Я знаю, что вы добавили много предостережений в свой пост, но вам лучше просто вычеркнуть первый ответ сейчас, многие новички просто придут к этому ответу и скопируют первое, что они увидят, не читая остальное. На самом деле вы не можете надежно сгенерировать UUID из Math.random API, и полагаться на это было бы опасно.
Вместо разделов «обновления» (для этого и предназначена история изменений) было бы лучше, если бы этот ответ был таким, как если бы он был написан сегодня.
Если вы В самом деле хотите, чтобы управление версиями оставалось встроенным, а не за историей ревизий, вы должны изменить порядок в обратном порядке: держать самый последний ответ первым.
«uuidv4» немного загадочен - я называю его «gitGudGuids» в своем проекте (шучу).
// 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('-');
Вот решение от 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
)
}
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 (random), поскольку в прошлый раз проблемы анализировал несовместимые UUID с помощью UUID Java.
Вот комбинация самый популярный ответ с обходным путем для Столкновения 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, если вы хотите его протестировать.
обратите внимание, что первая версия, одна `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.
Мне очень нравится, насколько чистая Ответ бруфа, но жаль, что плохие реализации Math.random оставляет шанс на столкновение.
Вот аналогичное решение, совместимое с RFC4122 версии 4, которое решает эту проблему путем смещения первых 13 шестнадцатеричных чисел на шестнадцатеричную часть временной метки и после истощения смещений на шестнадцатеричную часть микросекунд с момента загрузки страницы. Таким образом, даже если Math.random находится на одном семени, оба клиента должны будут сгенерировать UUID за одинаковое количество микросекунд с момента загрузки страницы (если поддерживается время высокой производительности) И за одну и ту же миллисекунду (или более 10 000 лет спустя) чтобы получить тот же UUID:
const { performance } = require('perf_hooks');
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.info(generateUUID())Имейте в виду, что new Date().getTime() не обновляется каждую миллисекунду. Я не уверен, как это влияет на ожидаемую случайность вашего алгоритма.
performance.now() are not limited to one-millisecond resolution. Instead, they represent times as floating-point numbers with up to микросекундная точность. Also unlike Date.now, the values returned by performance.now() всегда увеличивайте с постоянной скоростью, independent of the system clock which might be adjusted manually or skewed by software such as the Network Time Protocol.
Фактическое разрешение по времени может составлять или не составлять 17 мс (1/60 секунды), а не 1 мс.
Я настроил свой собственный генератор 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 <[email protected]>, 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));Лучший способ:
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,
? // generate 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}
Почему лучше?
Проект JavaScript на GitHub - https://github.com/LiosK/UUID.js
UUID.js The RFC-compliant UUID generator for JavaScript.
See RFC 4122 http://www.ietf.org/rfc/rfc4122.txt.
Features Generates RFC 4122 compliant UUIDs.
Version 4 UUIDs (UUIDs from random numbers) and version 1 UUIDs (time-based UUIDs) are available.
UUID object allows a variety of access to the UUID including access to the UUID fields.
Low timestamp resolution of JavaScript is compensated by random numbers.
Вот полностью несовместимая, но очень производительная реализация для генерации ASCII-безопасного GUID-подобного уникального идентификатора.
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');
Для тех, кому нужно решение, совместимое с RFC 4122 версии 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.info( UUID() );Вышеупомянутая функция должна иметь приличный баланс между скоростью и случайностью.
Простой модуль 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.info(Guid.newGuid());Использование:
Guid.newGuid()
"c6c2d12f-d76b-5739-e551-07e6de5b0807"
Guid.empty
"00000000-0000-0000-0000-000000000000"
Что беспокоит в ответах все, так это то, что кажется, что Ok для JavaScript хранит GUID как string. Ваш ответ, по крайней мере, касается более эффективного хранилища много с использованием Uint16Array. Функция toString должна использовать двоичное представление в JavaScript object.
Эти UUID, созданные этим кодом, либо слабо, но совместимы с RFC (_guid), либо строго, но не совместимы с RFC (_cryptoGuid). Первый использует Math.random (), который, как теперь известно, является плохим ГСЧ. Последний не устанавливает поля версии и варианта.
@broofa - Что бы вы посоветовали сделать, чтобы он соответствовал стандарту и RFC? И почему _cryptoGuid не соответствует RFC?
@Matt _cryptoGuid () устанавливает все 128 бит случайным образом, что означает, что он не устанавливает поля версии и варианта, как описано в RFC. См. Мою альтернативную реализацию uuidv4 (), которая использует crypto.getRandomValues () в моем ответе, получившем наибольшее количество голосов, выше, для сильной + совместимой реализации.
Самый быстрый 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).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).Ассамблея:
XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.XXXXXXXX и -XXXX-XXXX.XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX._p8(s), параметр s сообщает функции, следует ли добавлять дефисы или нет._p8() + _p8(true) + _p8(true) + _p8() и возвращаем его.Ссылка на этот пост в моем блоге
Наслаждаться! :-)
Эта реализация неверна. Некоторые символы GUID требуют особой обработки (например, 13-я цифра должна быть числом 4).
Я знаю, это старый вопрос. Для полноты картины, если ваша среда - это SharePoint, есть служебная функция SP.Guid.newGuid (Ссылка MSDN, которая создает новый 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;
};
В перенаправленном URL указано «Применимо к: SharePoint Foundation 2010»
Важно использовать хорошо протестированный код, который поддерживается более чем одним участником, вместо того, чтобы использовать для этого свой собственный материал. Это одно из тех мест, где вы, вероятно, захотите предпочесть наиболее стабильный код, чем кратчайшую возможную умную версию, которая работает в браузере X, но не учитывает особенности Y, которые часто приводят к очень сложным для исследования ошибкам, чем проявляются только случайным образом. для некоторых пользователей. Лично я использую uuid-js в https://github.com/aurigadl/uuid-js, который включен, поэтому я могу легко получать обновления.
Ответ broofa действительно хорош - впечатляюще умный, действительно ... совместимый с rfc4122, отчасти читаемый и компактный. Потрясающие!
Но если вы посмотрите на это регулярное выражение, эти многочисленные обратные вызовы replace(), вызовы функций toString() и Math.random() (где он использует только 4 бита результата, а остальное тратит впустую), вы можете начать задумываться о производительности. Действительно, joelpt даже решил отказаться от RFC для общей скорости GUID с помощью generateQuickGUID.
Но можем ли мы добиться соответствия скорости и RFC? Я говорю да! Можем ли мы сохранить читаемость? Ну ... Не совсем, но это легко, если вы последуете за мной.
Но сначала мои результаты по сравнению с broofa, guid (принятый ответ) и generateQuickGuid, не совместимым с RFC:
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-й итерации оптимизаций я превзошел самый популярный ответ на более чем 12X, принятый ответ на более чем 9X и быстрый несоответствующий ответ на 2-3X. И я все еще совместим с rfc4122.
Интересно как? Я выложил полный исходник на http://jsfiddle.net/jcward/7hyaC/3/ и на http://jsperf.com/uuid-generator-opt/4
Для объяснения начнем с кода broofa:
function broofa() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
return v.toString(16);
});
}
console.info(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.info(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.info(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.info(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.info(e5())Я попробовал e6 (), который обрабатывает 16 бит за раз, все еще используя 256-элементный LUT, и он показал убывающую отдачу от оптимизации. Хотя у него было меньше итераций, внутренняя логика была усложнена увеличением обработки, и он работал так же на настольных компьютерах и только на ~ 10% быстрее на мобильных устройствах.
Последний прием оптимизации - развернуть цикл. Поскольку мы выполняем цикл фиксированное количество раз, технически мы можем записать все это вручную. Я попробовал это однажды с единственной случайной величиной r, которую я все время переназначал, и производительность упала. Но с четырьмя переменными, которым заранее назначены случайные данные, затем с использованием таблицы поиска и применения правильных битов RFC, эта версия курит их все:
var lut = []; for (var i=0; i<256; i++) { lut[i] = (i<16?'0':'')+(i).toString(16); }
function e7()
{
var d0 = Math.random()*0xffffffff|0;
var d1 = Math.random()*0xffffffff|0;
var d2 = Math.random()*0xffffffff|0;
var d3 = Math.random()*0xffffffff|0;
return lut[d0&0xff]+lut[d0>>8&0xff]+lut[d0>>16&0xff]+lut[d0>>24&0xff]+'-'+
lut[d1&0xff]+lut[d1>>8&0xff]+'-'+lut[d1>>16&0x0f|0x40]+lut[d1>>24&0xff]+'-'+
lut[d2&0x3f|0x80]+lut[d2>>8&0xff]+'-'+lut[d2>>16&0xff]+lut[d2>>24&0xff]+
lut[d3&0xff]+lut[d3>>8&0xff]+lut[d3>>16&0xff]+lut[d3>>24&0xff];
}
console.info(e7())Модулированный: http://jcward.com/UUID.js - UUID.generate()
Забавно то, что создание 16 байтов случайных данных - самая простая часть. Весь трюк выражается в строковом формате с соблюдением RFC, и наиболее точно он достигается с помощью 16 байтов случайных данных, развернутого цикла и таблицы поиска.
Надеюсь, моя логика верна - очень легко ошибиться в такой утомительной работе с битами. Но результаты мне нравятся. Надеюсь, вам понравилась эта безумная поездка по оптимизации кода!
Обратите внимание: Моей основной целью было показать и обучить потенциальным стратегиям оптимизации. Другие ответы охватывают важные темы, такие как столкновения и действительно случайные числа, которые важны для генерации хороших UUID.
Этот код все еще содержит пару ошибок: строки Math.random()*0xFFFFFFFF должны быть Math.random()*0x100000000 для полной случайности, а >>>0 следует использовать вместо |0, чтобы значения оставались беззнаковыми (хотя с текущим кодом, я думаю, все уходит нормально, даже если они подписаны) . Наконец, в наши дни было бы очень хорошей идеей использовать window.crypto.getRandomValues, если он доступен, и возвращаться к Math.random только в случае крайней необходимости. Math.random может иметь менее 128 бит энтропии, и в этом случае это будет более уязвимо для столкновений, чем необходимо.
Могу я просто сказать - я не могу сосчитать, сколько раз я указывал разработчикам на этот ответ, потому что он так красиво указывает на компромисс между производительностью, элегантностью кода и удобочитаемостью. Спасибо, Джефф.
Я не знаю, изменился ли ответ @Broofa с момента запуска этих тестов (или изменились ли движки браузера, запускающие тесты - прошло пять лет), но я просто запустил их оба в двух разных сервисах тестирования (jsben.ch и jsbench.github.io), и в каждом случае ответ Бруфа (с использованием Math.random) был быстрее, чем эта версия e7 () на 30-35%.
Он основан на дате и добавляет случайный суффикс, чтобы «гарантировать» уникальность. Хорошо работает с идентификаторами 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;
};
Я использую эту функцию ниже, надеюсь, она может быть полезной.
function NewGuid()
{
var sGuid = "";
for (var i=0; i<32; i++)
{
sGuid+=Math.floor(Math.random()*0xF).toString(0xF);
}
return sGuid;
}
Для моего варианта использования мне потребовалась генерация идентификатора, который гарантированно был уникальным в глобальном масштабе; без исключений. Некоторое время я боролся с этой проблемой и нашел решение под названием tuid (действительно уникальный идентификатор). Он генерирует идентификатор, в котором первые 32 символа генерируются системой, а оставшиеся цифры представляют миллисекунды с начала эпохи. В ситуациях, когда мне нужно сгенерировать идентификаторы на клиентском javascript, это работает хорошо. Посмотри:
https://github.com/mongoh/tuid
Это буквально вызывает AWS S3 для получения случайного идентификатора. Если вызов сервера в порядке, вы можете просто попросить сервер сгенерировать UUID.
Что ж, на это уже есть куча ответов, но, к сожалению, в этой группе нет "истинного" случайного. Приведенная ниже версия является адаптацией ответа 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 <[email protected]>, 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 <[email protected]>, 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);
});
};
Я хотел понять ответ Брофа, поэтому расширил его и добавил комментарии:
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 с объяснением эквивалентов очень полезен.
Простым решением для создания уникальной идентификации является использование маркера времени и добавление к нему случайного числа. Я предпочитаю ставить перед ним "uuid -".
Ниже функция сгенерирует случайную строку типа: uuid-14d93eb1b9b4533e6. Не нужно генерировать случайную строку из 32 символов. В этом случае случайная строка из 16 символов более чем достаточна для предоставления уникальных UUID в javascript.
var createUUID = function() {
return"uuid-"+((new Date).getTime().toString(16)+Math.floor(1E7*Math.random()).toString(16));
}
Простой код, использующий crypto.getRandomValues(a) на поддерживаемые браузеры (IE11 +, iOS7 +, FF21 +, Chrome, Android Chrome). Избегает использования Math.random(), потому что это может вызвать коллизии (например, 20 коллизий для 4000 сгенерированных uuid в реальной ситуации 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);
}
Примечания:
Просто подумал, что выложу еще один способ сделать то же самое.
function guid() {
var chars = ["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"];
var str = "";
for(var i=0;i<36;i++) {
var str = str + ((i == 8 || i == 13 || i == 18 || i == 23) ? "-" : chars[Math.floor(Math.random()*chars.length)]);
};
return str;
}
Вы можете использовать node-uuid (https://github.com/kelektiv/node-uuid)
Простое и быстрое создание RFC4122 UUIDS.
Функции:
Установить с помощью NPM:
npm install uuid
Или используя uuid через браузер:
Скачать Raw File (uuid v1): https://raw.githubusercontent.com/kelektiv/node-uuid/master/v1.js Скачать Raw File (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();
const { v4: uuidv4 } = require('uuid'); and ES6: import { v4 as uuidv4 } from 'uuid';Вы можете использовать пакет npm гид, генератор руководств и валидатор.
https://www.npmjs.com/package/guid
Пример:
Guid.raw();
// -> '6fdf6ffc-ed77-94fa-407e-a7b86ed9e59d'
Обновлено: Этот пакет устарел. Вместо этого используйте uuid.
https://www.npmjs.com/package/uuid
Пример:
const uuidv4 = require('uuid/v4');
uuidv4(); // ⇨ '10ba038e-48da-487b-96e8-8d3b99b6d18a'
На всякий случай, если кто-то, заходящий в Google, ищет небольшую служебную библиотеку, ShortId (https://www.npmjs.com/package/shortid) отвечает всем требованиям этого вопроса. Он позволяет указывать допустимые символы и длину и гарантирует непоследовательные и неповторяющиеся строки.
Чтобы сделать это более реальным ответом, ядро этой библиотеки использует следующую логику для создания своих коротких идентификаторов:
function encode(lookup, number) {
var loopCounter = 0;
var done;
var str = '';
while (!done) {
str = str + lookup( ( (number >> (4 * loopCounter)) & 0x0f ) | randomByte() );
done = number < (Math.pow(16, loopCounter + 1 ) );
loopCounter++;
}
return str;
}
/** Generates the short id */
function generate() {
var str = '';
var seconds = Math.floor((Date.now() - REDUCE_TIME) * 0.001);
if (seconds === previousSeconds) {
counter++;
} else {
counter = 0;
previousSeconds = seconds;
}
str = str + encode(alphabet.lookup, version);
str = str + encode(alphabet.lookup, clusterWorkerId);
if (counter > 0) {
str = str + encode(alphabet.lookup, counter);
}
str = str + encode(alphabet.lookup, seconds);
return str;
}
Я не редактировал это, чтобы отразить Только самых основных частей этого подхода, поэтому приведенный выше код включает некоторую дополнительную логику из библиотеки. Если вам интересно, что он делает, взгляните на источник: https://github.com/dylang/shortid/tree/master/lib
Привет, вот рабочий пример, он генерирует 32-значный уникальный UUID.
function generateUUID() {
var d = new Date();
var k = d.getTime();
var str = k.toString(16).slice(1)
var UUID= 'xxxx-xxxx-4xxx-yxxx-xzx'.replace(/[xy]/g, function (c)
{
var r = Math.random() * 16 | 0;
v = c == 'x' ? r : (r & 3 | 8);
return v.toString(16);
});
var newString = UUID.replace(/[z]/, str)
return newString;
}
var x = generateUUID()
console.info(x,x.length)
Я нашел этот скрипт полезным для создания GUID в JavaScript.
https://github.com/addui/GUIDJS
var myGuid = GUID();
Это использует Math.random под капотом. Поэтому в браузерах с плохой реализацией Math.random могут возникать конфликты и поломки. Предпочитайте uuid, поскольку он использует API crypto там, где это возможно.
Это может кому-то пригодиться ...
var d = new Date().valueOf();
var n = d.toString();
var result = '';
var length = 32;
var p = 0;
var chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
for (var i = length; i > 0; --i){
result += ((i & 1) && n.charAt(p) ? '<b>' + n.charAt(p) + '</b>' : chars[Math.floor(Math.random() * chars.length)]);
if (i & 1) p++;
};
function randomHex(length) {
var random_string = '';
if (!length){
length = 1;
}
for(var i=0; i<length; i+=1){
random_string += Math.floor(Math.random() * 15).toString(16);
}
return random_string;
}
function guid() {
return randomHex(8);
}
Здесь вы можете найти очень маленькую функцию, которая генерирует uuids https://gist.github.com/jed/982883
Одна из последних версий:
function b(
a // placeholder
){
var cryptoObj = window.crypto || window.msCrypto; // for IE 11
return a // if the placeholder was passed, return
? ( // a random number from 0 to 15
a ^ // unless b is 8,
cryptoObj.getRandomValues(new Uint8Array(1))[0] // 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
)
}
Если вам просто нужна случайная 128-битная строка без определенного формата, вы можете использовать:
function uuid() {
return crypto.getRandomValues(new Uint32Array(4)).join('-');
}
Что вернет что-то вроде 2350143528-4164020887-938913176-2513998651.
Кстати, почему он генерирует только числа, а не символы? гораздо менее безопасный
вы также можете добавлять символы (буквы), например: Array.from((window.crypto || window.msCrypto).getRandomValues(new Uint32Array(4))).map(n => n.toString(16)).join('-')
let uniqueId = Date.now().toString(36) + Math.random().toString(36).substring(2);
document.getElementById("unique").innerHTML =
Math.random().toString(36).substring(2) + (new Date()).getTime().toString(36);<div id = "unique">
</div>Если идентификаторы генерируются с интервалом более 1 миллисекунды, они на 100% уникальны.
Если два идентификатора генерируются с более короткими интервалами и при условии, что случайный метод действительно случайный, это приведет к генерации идентификаторов, которые с вероятностью 99,99999999999999% будут глобально уникальными (коллизия в 1 из 10 ^ 15)
Вы можете увеличить это число, добавив больше цифр, но для генерации 100% уникального идентификатора вам понадобится глобальный счетчик.
если вам нужна совместимость с RFC, это форматирование будет передаваться как действительный GUID версии 4:
let u = Date.now().toString(16) + Math.random().toString(16) + '0'.repeat(16);
let guid = [u.substr(0,8), u.substr(8,4), '4000-8' + u.substr(13,3), u.substr(16,12)].join('-');
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('-');
document.getElementById("unique").innerHTML = guid;<div id = "unique">
</div>Edit: The above code follow the intention, but not the letter of the RFC. Among other discrepancies it's a few random digits short. (Add more random digits if you need it) The upside is that this it's really fast :) You can test validity of your GUID here
Но это не UUID?
Нет. UUID / GUID - это 122-битное (+ шесть зарезервированных битов) число. он может гарантировать уникальность через службу глобального счетчика, но часто он передает время, MAC-адрес и случайность. UUID не случайны! Предлагаемый здесь UID не полностью сжат. Вы можете сжать его до 122-битного целого числа, добавить 6 предопределенных битов и дополнительные случайные биты (удалить несколько битов таймера), и вы получите идеально сформированный UUID / GUID, который вам затем придется преобразовать в шестнадцатеричный. Для меня это не добавляет ничего, кроме соответствия длине идентификатора.
Ретрансляция MAC-адресов для уникальности на виртуальных машинах - плохая идея!
Я делаю что-то подобное, но с начальными символами и некоторыми дефисами (например, [slug, date, random].join("_") для создания usr_1dcn27itd_hj6onj6phr. Это делает так, что идентификатор также удваивается как поле "создано в"
Основываясь на комментарии @ SephReed, я думаю, что сначала указать дату - это хорошо, поскольку она сортируется в хронологическом порядке, что может принести пользу позже при сохранении или индексировании идентификаторов.
Для интересующихся: toString(36) преобразуется в систему счисления по основанию 36 (0..9a..z). Пример: (35).toString(36) - это z.
ES6 образец
const guid=()=> {
const s4=()=> Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
return `${s4() + s4()}-${s4()}-${s4()}-${s4()}-${s4() + s4() + s4()}`;
}
Еще один более читаемый вариант всего с двумя мутациями.
function uuid4()
{
function hex (s, b)
{
return s +
(b >>> 4 ).toString (16) + // high nibble
(b & 0b1111).toString (16); // low nibble
}
let r = crypto.getRandomValues (new Uint8Array (16));
r[6] = r[6] >>> 4 | 0b01000000; // Set type 4: 0100
r[8] = r[8] >>> 3 | 0b10000000; // Set variant: 100
return r.slice ( 0, 4).reduce (hex, '' ) +
r.slice ( 4, 6).reduce (hex, '-') +
r.slice ( 6, 8).reduce (hex, '-') +
r.slice ( 8, 10).reduce (hex, '-') +
r.slice (10, 16).reduce (hex, '-');
}
Что ж, большинство разработчиков js - веб-разработчики, и мы не понимаем, что делают побитовые операторы, потому что мы не используем их в большинстве случаев, когда разрабатываем. На самом деле они мне никогда не понадобились, а я занимаюсь js с 1997 года. Таким образом, ваш примерный код по-прежнему совершенно нечитаем для среднего веб-разработчика, который его прочитает. Не говоря уже о том, что вы по-прежнему используете однобуквенные имена переменных, что делает его еще более загадочным. Наверное, прочтите чистый код, может быть, это поможет: amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/…
@ inf3rno, не ругайте его, все предлагаемые решения в этой ветке загадочны, но это правильные ответы, учитывая, что вопрос заключался в том, чтобы иметь своего рода однострочник. вот что такое загадочные однострочные. они не могут позволить себе быть удобочитаемыми для среднего разработчика, но они экономят место на экране там, где достаточно простого предыдущего комментария. И в результате он становится намного более читабельным, чем если бы он был в «читаемом коде».
@ user1529413 Да. Уникальность требует индекса.
Это мой любимый ответ, потому что он создает UUID как 16-байтовое (128-битное) значение, а не его сериализованную, удобную для чтения форму. Было бы тривиально легко отбросить строковый материал и просто установить правильные биты случайного 128-битного, что и должно быть для uuidv4. Вы можете использовать base64 для более коротких URL-адресов, передать его обратно какой-либо веб-сборке, сохранить его в меньшем объеме памяти, чем в виде строки, сделать его буфером размером 4096 и поместить в него 256 uuid, сохранить в базе данных браузера и т. д. Намного лучше чем иметь с самого начала все как длинную строку в шестнадцатеричном коде в нижнем регистре.
Для тех, кто использует Javascript в Windows (например, WScript / CScript / MSHTA). Можно использовать ActiveX. В частности, объект Scriptlet.Typelib:
WScript.Echo((new ActiveXObject("Scriptlet.TypeLib")).Guid)
Обратите внимание, что этот ответ работает только с перечисленными мною технологиями, он не будет работать ни в одном браузере, даже в Microsoft Edge! Итак, ваш опыт будет зависеть от этого ответа.
Такой подход все еще актуален в 2018 году? Вау :-)
Основываясь на работе broofa, я добавил немного случайности, добавив метку времени в math.random().
Надеюсь, это поможет
function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = parseFloat('0.' + Math.random().toString().replace('0.', '') + new Date().getTime()) * 16 | 0,
v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
Для науки. Я еще не видел, чтобы кто-то делал это ... он не совместим с v4, но его можно легко изменить. Это просто пример расширения типа Uint8Array и использования crypto.getRandomValues () для генерации байтовых значений uuid.
class uuid extends Uint8Array {
constructor() {
super(16)
/* not v4, just some random bytes */
window.crypto.getRandomValues(this)
}
toString() {
let id = new String()
for (let i = 0; i < this.length; i++) {
/*convert uint8 to hex string */
let hex = this[i].toString(16).toUpperCase()
/*add zero padding*/
while (hex.length < 2) {
hex = String(0).concat(hex)
}
id += hex
/* add dashes */
if (i == 4 || i == 6 || i == 8 || i == 10 || i == 16){
id += '-'
}
}
return id
}
}
Я не смог найти ни одного ответа, который использует один 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.
Обратите внимание на тот факт, что на момент написания этого документа getRandomValues не реализован для объекта crypto в Node.js. Однако он имеет эквивалентную функцию randomBytes, которую можно использовать вместо нее.
Хорошо, используя пакет uuid, он поддерживает версии 1, 3, 4 и 5 UUID:
yarn add uuid
а потом:
const uuidv1 = require('uuid/v1');
uuidv1(); // ⇨ '45745c60-7b1a-11e8-9c9c-2d42b21b1a3e'
Вы также можете сделать это с помощью полностью определенных опций:
const v1options = {
node: [0x01, 0x23, 0x45, 0x67, 0x89, 0xab],
clockseq: 0x1234,
msecs: new Date('2011-11-01').getTime(),
nsecs: 5678
};
uuidv1(v1options); // ⇨ '710b962e-041c-11e1-9234-0123456789ab'
Для получения дополнительной информации посетите страницу npm здесь
Мы можем использовать replace и crypto.getRandomValues, чтобы получить такой вывод:
xxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxxxxxx
Если мы ищем оптимальное решение, мы должны заменить crypto.getRandomValues(new Uint8Array(1))[0] массивом (32).
const uuidv4 = () =>
([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
);
console.info(uuidv4());
Чтобы получить этот код:
function uuidv4() {
let bytes = window.crypto.getRandomValues(new Uint8Array(32));
const randomBytes = () => (bytes = bytes.slice(1)) && bytes[0];
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
(c ^ randomBytes() & 15 >> c / 4).toString(16)
);
}
for (var i = 0; i < 10; i++)
console.info(uuidv4());Мы можем сделать, как Google Analytics, и добавить метку времени с помощью: uuidv4() + "." + (+new Date()).
UUID в настоящее время имеет предложение по добавлению к стандартной библиотеке и может поддерживаться здесь https://github.com/tc39/proposal-uuid
Предложение включает в себя следующий UUID:
// We're not yet certain as to how the API will be accessed (whether it's in the global, or a
// future built-in module), and this will be part of the investigative process as we continue
// working on the proposal.
uuid(); // "52e6953d-edbe-4953-be2e-65ed3836b2f0"
Эта реализация следует той же схеме, что и генерация случайных uuid V4, найденная здесь: https://www.npmjs.com/package/uuid
const uuidv4 = require('uuid/v4');
uuidv4(); // ⇨ '1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed'
Я думаю, что стоит понять, сколько пропускной способности можно сэкономить, имея официальную реализацию в стандартной библиотеке. Авторы предложения также отметили:
Модуль uuid размером 12 КБ загружается из числа npm> 62 000 000 раз в месяц (июнь 2019 г.); делая его доступным в стандартной библиотеке, в конечном итоге экономит ТБ полосы пропускания во всем мире. Если мы продолжим удовлетворять потребности пользователей, такие как uuid, с помощью стандартной библиотеки, экономия полосы пропускания увеличится.
Машинописная версия обновления broofa от 28.06.2017, основанная на API crypto:
function genUUID() {
// Reference: https://stackoverflow.com/a/2117523/709884
return ("10000000-1000-4000-8000-100000000000").replace(/[018]/g, s => {
const c = Number.parseInt(s, 10)
return (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
})
}
Причины:
+ между number[] и number недопустимоstring в number должно быть явным.Старый вопрос, так что это может никогда не всплыть наверх, но я построил на всем упомянутом здесь, чтобы создать что-то вдвое более быстрое, переносимое для всех сред, включая узел, и обновлено с Math.random () до случайности криптостойкости. Вы можете подумать, что uuid не нуждается в криптостойкости, но это означает, что вероятность столкновения еще меньше, а это и есть суть uuid.
function random() {
const
fourBytesOn = 0xffffffff, // 4 bytes, all 32 bits on: 4294967295
c = typeof crypto === "object"
? crypto // node or most browsers
: typeof msCrypto === "object" // stinky non-standard IE
? msCrypto // eslint-disable-line no-undef
: null; // what old or bad environment are we running in?
return c
? c.randomBytes
? parseInt(c.randomBytes(4).toString("hex"), 16) / (fourBytesOn + 1) - Number.EPSILON // node
: c.getRandomValues(new Uint32Array(1))[0] / (fourBytesOn + 1) - Number.EPSILON // browsers
: Math.random();
}
function uuidV4() { // eslint-disable-line complexity
// if possible, generate a single random value, 128 bits (16 bytes) in length
// in an environment where that is not possible, generate and make use of 4 32-bit (4-byte) random values
// use crypto-grade randomness when available, else Math.random()
const
c = typeof crypto === "object"
? crypto // node or most browsers
: typeof msCrypto === "object" // stinky non-standard IE
? msCrypto // eslint-disable-line no-undef
: null; // what old or bad environment are we running in?
let
byteArray = c
? c.randomBytes
? c.randomBytes(16) // node
: c.getRandomValues(new Uint8Array(16)) // browsers
: null,
uuid = [ ];
/* eslint-disable no-bitwise */
if ( ! byteArray) { // no support for generating 16 random bytes in one shot -- this will be slower
const
int = [
random() * 0xffffffff | 0,
random() * 0xffffffff | 0,
random() * 0xffffffff | 0,
random() * 0xffffffff | 0
];
byteArray = [ ];
for (let i = 0; i < 256; i++) {
byteArray[i] = int[i < 4 ? 0 : i < 8 ? 1 : i < 12 ? 2 : 3] >> i % 4 * 8 & 0xff;
}
}
byteArray[6] = byteArray[6] & 0x0f | 0x40; // always 4, per RFC, indicating the version
byteArray[8] = byteArray[8] & 0x3f | 0x80; // constrained to [89ab], per RFC for version 4
for (let i = 0; i < 16; ++i) {
uuid[i] = (byteArray[i] < 16 ? "0" : "") + byteArray[i].toString(16);
}
uuid =
uuid[ 0] + uuid[ 1] + uuid[ 2] + uuid[ 3] + "-" +
uuid[ 4] + uuid[ 5] + "-" +
uuid[ 6] + uuid[ 7] + "-" +
uuid[ 8] + uuid[ 9] + "-" +
uuid[10] + uuid[11] + uuid[12] + uuid[13] + uuid[14] + uuid[15];
return uuid;
/* eslint-enable no-bitwise */
}
Ни в коем случае не используйте Math.random, поскольку он генерирует некриптографический источник случайных чисел.
Решение ниже с использованием crypto.getRandomValues
function uuidv4() {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
// tslint:disable-next-line: no-bitwise
const r =
(window.crypto.getRandomValues(new Uint32Array(1))[0] *
Math.pow(2, -32) * 16) |
0;
// tslint:disable-next-line: no-bitwise
const v = c === "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
Этот связь помогает вам понять небезопасную случайность, создаваемую Fortify Scanner
Собственный URL.createObjectURL генерирует uuid. Вы можете этим воспользоваться.
function uuid() {
const url = URL.createObjectURL(new Blob())
const [id] = url.toString().split('/').reverse()
URL.revokeObjectURL(url)
return id
}
работает как шарм. Лучше, чем пытаться сгенерировать вручную. Очень умный!
Производительность довольно плохая, но в зависимости от случая может хватить
Для самого быстрого комбинированного генератора, который совместим с синхронизацией узла, монотонным по времени и т.д. -версия, 48-битный идентификатор узла и 13-битный тактовый сигнал с 3-битным uuid-вариантом. - <br> Комбинирование использования uuid4 для записи BigInt и связанное с ним преобразование работает очень быстро с ntohl подход здесь. - <br> При желании я могу предоставить код.
Поскольку я могу, я подумал, что должен поделиться своим решением, так как это очень интересная проблема, и у нее так много решений.
Это работает и для nodejs, если вы замените let buffer = new Uint8Array(); crypto.getRandomValues на let buffer = crypto.randomBytes(16)
Надеюсь, это кому-то поможет. Он должен превзойти большинство решений с регулярными выражениями по производительности.
const hex = '0123456789ABCDEF'
let generateToken = function() {
let buffer = new Uint8Array(16)
crypto.getRandomValues(buffer)
buffer[6] = 0x40 | (buffer[6] & 0xF)
buffer[8] = 0x80 | (buffer[8] & 0xF)
let segments = []
for (let i = 0; i < 16; ++i) {
segments.push(hex[(buffer[i] >> 4 & 0xF)])
segments.push(hex[(buffer[i] >> 0 & 0xF)])
if (i == 3 || i == 5 || i == 7 || i == 9) {
segments.push('-')
}
}
return segments.join('')
}
for (let i = 0; i < 100; ++i) {
console.info(generateToken())
}Графики производительности, их все любят: jsbench
Удачи и спасибо за все остальные решения, некоторые из них прослужили мне довольно долго.
var guid = createMyGuid();
function createMyGuid()
{
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);
});
}
В чем разница с эта почта?
@fcdt - разница c === 'x' вместо c == 'x'.
UUID со встроенной меткой времени (эмиттер / парсер)
Я также опубликую свой простой подход к созданию действительного UUID v4 с очень сильной уникальностью и быстрым временем выполнения. Основная идея не нова, но подход другой. Я использую временную метку в миллисекундах из date.now() (в библиотеке Node.js, на которую я укажу позже, я использую временную метку в наносекундах из process.hrtime.bigint()), а затем добавляю случайное 5-значное число (10000-90000) в конец строки временной метки. После объединения строк я просто формирую действительный UUID из цифр и пары специальных символов, так что мой UUID состоит только из цифр и нескольких нечисловых символов. Пожалуйста, проверьте это ниже:
/*
* uuid-timestamp (emitter)
* UUID v4 based on timestamp
*
* Created by tarkh
* tarkh.com (C) 2020
*/
const uuidEmit = () => {
// Get now time
const n = Date.now();
// Generate random
const r = Math.random();
// Stringify now time and generate additional random number
const s = String(n) + String(~~(r*9e4)+1e4);
// Form UUID and return it
return `${s.slice(0,8)}-${s.slice(8,12)}-4${s.slice(12,15)}-${[8,9,'a','b'][~~(r*3)]}${s.slice(15,18)}-${s.slice(s.length-12)}`;
};
// Generate 5 UUIDs
console.info(`${uuidEmit()}
${uuidEmit()}
${uuidEmit()}
${uuidEmit()}
${uuidEmit()}`);Глядя на результаты, вы, очевидно, видите, что первая часть UUID совпадает, а затем идет случайность. Это потому, что я вставил метку времени в UUID линейно. Код будет генерировать новый UUID каждую миллисекунду (наносекунда в библиотеке Node.js) + добавлять случайное 5-значное число в конец, поэтому мы получаем очень приблизительную вероятность столкновения около 1 из 10 миллионов в секунду. Если мы используем библиотеку Node.js, наша приблизительная вероятность столкновения составляет 1 из 10 миллиардов в секунду.
Отметка времени встроена в UUID
Поскольку мы вставляем метку времени в UUID линейно, мы получаем особенность (хорошая или плохая - в зависимости от задачи) - возможность легко извлечь эту метку времени обратно из UUID. Таким образом мы можем понять, когда был выпущен UUID:
/*
* uuid-timestamp (parser)
* UUID v4 based on timestamp
*
* Created by tarkh
* tarkh.com (C) 2020
*/
const uuidParse = (uuid) => {
// Get current timestamp string length
let tl = String(Date.now()).length;
// Strip out timestamp from UUID
let ts = '';
let i = -1;
while(tl--) {
i++;
if (i===8||i===13||i===14||i===18||i===19||i===23) {
tl++;
continue;
}
ts += uuid[i];
}
return Number(ts);
};
// Get the timestamp when UUID was emitted
const time = uuidParse('15970688-7109-4530-8114-887109530114');
// Covert timestamp to date and print it
console.info(new Date(time).toUTCString());Node.js
Версия моего кода выше NPM доступна как Модуль Node.js. Эта версия еще более эффективна в создании уникальных значений, потому что вместо миллисекундной отметки времени она использует nanoseconds из комбинации системного времени и process.hrtime.bigint() diff.
Контрольные точки
В конце своего сообщения я хочу провести несколько тестов производительности на основе некоторых ответов из этой темы. Конечно, мое решение не самое быстрое, но, безусловно, занимает лидирующие позиции.
Проверьте jsBench здесь
Это технически, не соответствующий UUID4. Приятно, что у него есть отметка времени, но на этом этапе лучше использовать UUID1. В этой схеме вероятность столкновения составляет 1 из 10 000 каждый раз, когда происходит столкновение в миллисекундах, а если вы будете генерировать события каждую секунду, это будет 1/1000 или 1 из 10 миллионов каждую секунду. Это почти гарантированно произойдет в любом производстве, которое больше, чем просто хобби. Семантически UUID не должны быть полями отметок времени. Вот для чего нужны поля отметок времени. Они предназначены для того, чтобы «дать мне идентификатор, который НИКОГДА не столкнется».
@DeusXMachina спасибо за комментарий, я на 100% согласен. Вот почему в библиотеке NodeJS, на которую я указал в конце своего сообщения, я использую process.hrtime.bigint() для временных меток, что дает мне наносекундную шкалу (1 миллион вероятностей в секунду) + случайное число от 10000 до 90000 в конце. В конце концов, если мои расчеты верны, это переходит к многомиллиардному шансу за 1 секунду. В любом случае это нестандартный подход с конкретным вариантом использования, как я уже упоминал выше.
Ах, я это пропустил. Возможно, отредактируйте свое объяснение, чтобы было более очевидно, что вы используете разрешение ns. «... создавать новый UUID каждую миллисекунду + добавлять случайное 5-значное число». Тем не менее, почему бы не использовать uuid1? Это обеспечивает разрешение 0,1 мкс плюс еще 48 бит энтропии. en.wikipedia.org/wiki/…
Недействительным должно быть xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
@NVRM Действителен, вы можете проверить результат в любых онлайн-валидаторах UUID, то есть здесь: freecodeformat.com/validate-uuid-guid.php
Некромантинг.
Фактически, Guid, или UUID, как его называют в кругах, отличных от microsoft, представляет собой всего лишь 128-битное криптографическое случайное число с номером версии uuid (1-5), находящимся в байтах с фиксированным местоположением.
Итак, когда вы просто генерируете кучу случайных чисел от 0 до 65535 и кодируете их в шестнадцатеричном формате, например:
function guid()
{
function s4()
{
return Math.floor(Math.random() * 65536).toString(16).padStart(4, '0')
} // End Function s4
return s4() + s4() + '-' + s4() + '-' + "4" + s4().substr(1) + '-' + s4() + '-' + s4() + s4() + s4();
} // End Function guid
вы получаете действительный GUID, но из-за случайной реализации он не является криптографически безопасным.
Чтобы сгенерировать криптографически безопасный GUID, вам необходимо использовать window.crypto (или window.msCrypto для Internet Exploder).
Это выглядит так:
function cryptGuid()
{
var array = new Uint16Array(8);
(window.crypto || window.msCrypto).getRandomValues(array);
var dataView = new DataView(array.buffer);
var parts = [];
for(var i = 0; i < array.length; ++i)
{
// 0&1,2,3,4,5-7 dataView.getUint16(0-7)
if (i>1 && i<6) parts.push("-");
parts.push(dataView.getUint16(i).toString(16).padStart(4, '0'));
}
parts[5] = "4" + parts[5].substr(1);
// console.info(parts);
return parts.join('').toUpperCase();// .toLowerCase();
}
cryptGuid();
Кроме того, вам нужно решить, возвращать ли число в виде строки символов в нижнем или верхнем регистре. Некоторое программное обеспечение требует символов нижнего регистра (например, Reporting Service), в то время как другое генерирует символы верхнего регистра (SQL-Server).
Здесь есть много правильных ответов, но, к сожалению, включенные примеры кода довольно загадочны и трудны для понимания. Вот как я создаю (случайные) UUID версии 4.
Обратите внимание, что следующие фрагменты кода используют двоичные литералы для улучшения читаемости, поэтому требуется ECMAScript 6.
function uuid4() {
let array = new Uint8Array(16)
crypto.randomFillSync(array)
// manipulate 9th byte
array[8] &= 0b00111111 // clear first two bits
array[8] |= 0b10000000 // set first two bits to 10
// manipulate 7th byte
array[6] &= 0b00001111 // clear first four bits
array[6] |= 0b01000000 // set first four bits to 0100
const pattern = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
let idx = 0
return pattern.replace(
/XX/g,
() => array[idx++].toString(16).padStart(2, "0"), // padStart ensures leading zero, if needed
)
}
Только 2-я строка отличается.
function uuid4() {
let array = new Uint8Array(16)
crypto.getRandomValues(array)
// manipulate 9th byte
array[8] &= 0b00111111 // clear first two bits
array[8] |= 0b10000000 // set first two bits to 10
// manipulate 7th byte
array[6] &= 0b00001111 // clear first four bits
array[6] |= 0b01000000 // set first four bits to 0100
const pattern = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
let idx = 0
return pattern.replace(
/XX/g,
() => array[idx++].toString(16).padStart(2, "0"), // padStart ensures leading zero, if needed
)
}
И, наконец, соответствующие тесты (Жасмин).
describe(".uuid4()", function() {
it("returns a UUIDv4 string", function() {
const uuidPattern = "XXXXXXXX-XXXX-4XXX-YXXX-XXXXXXXXXXXX"
const uuidPatternRx = new RegExp(uuidPattern.
replaceAll("X", "[0-9a-f]").
replaceAll("Y", "[89ab]"))
for (let attempt = 0; attempt < 1000; attempt++) {
let retval = uuid4()
expect(retval.length).toEqual(36)
expect(retval).toMatch(uuidPatternRx)
}
})
})
Очень хорошее объяснение UUID версии 4 находится здесь: https://www.cryptosys.net/pki/uuid-rfc4122.html.
Также существует множество сторонних пакетов. Однако, если у вас есть только базовые потребности, я их не рекомендую. На самом деле выигрывать особо нечего, а терять очень много. Авторы могут преследовать малейшие кусочки производительности, «исправлять» вещи, которые не должны быть исправлены, а когда дело доходит до безопасности, это рискованная идея. Точно так же они могут вносить другие ошибки или несовместимости. Тщательные обновления требуют времени.
Следующий uuidвыполнение предлагает решение разные ES6 2020 с использованием BigInt и фокусируется на «намерении варианта использования для шаблона проектирования uuid»; особенно для использования со сценариями indexedDbprimaryKey, где ценно объединение секвенирования по времени и сопоставления.
So, noting that this post has over 30 answers, here goes...
This post has:
- a "TL;DR"
codesection w/self-contained es6class Xuid- a use-case and motivationsdiscussionsection regarding the es6
class Xuidprovided code.
class Xuidдля общего v4 uuid с использованием монотонных часовПриведенный ниже код извлечен из библиотеки веб-клиента EdgeS Smallscript, которую я написал и которой владею, и предоставляется здесь под свободной лицензией MIT. версия на github будет доступен после выпуска набора инструментов веб-клиента EdgeS.
Usage example:
eval:
console.info(Xuid.v4New)
emits:{1eb4a659-8bdc-4ce0-c002-b1d505d38ea8}
class Xuid {
//@ edges.sm.st, ess.dev: MIT license Smallscript/David Simmons 2020
//! Can't use `static const field = const` xbrowser (thus, const's duped)
static get v4New() {
const ns7Now = this.ns7Now, xnode48 = this.xnode48; let clock_seq13
// monotonic `clock_seq` guarantee (13-bits/time-quantum)
if (ns7Now <= this.ns7Now_prevSeq && this.ns7Now_prevSeq)
clock_seq13 = ((this.ns7Now_prevSeq += 1n) - ns7Now) & 0b1_1111_1111_1111n
else
clock_seq13 = 0n, this.ns7Now_prevSeq = ns7Now
const time60 = ((ns7Now << 4n) & 0xFFFF_FFFF_FFFF_0000n) |
(ns7Now & 0x0000_0000_0000_0FFFn),
v4 = 0x1_00000000_0000_0000_0000_000000000000n |
(time60 << 64n) | (0x00000000_0000_4000_0000_000000000000n) | // M: V4
(0b110n << 61n) | (clock_seq13 << 48n) | // N: Variant-2 time-seq collation
xnode48, s = v4.toString(16)//.substr(1)
return `{${s.substr(1,8)}-${s.substr(9,4)}-${s.substr(13,4)}-${
s.substr(17,4)}-${s.substr(21,12)}}`
}
static get xnode48()/*:<BigInt#48>*/{
if (this.xnode48_) return this.xnode48_
let clockSeqNode; if (typeof URL !== 'undefined' && URL.createObjectURL) {
const url = URL.createObjectURL(new Blob())
const id = (url.toString().split('/').reverse()[0]).split('-')
URL.revokeObjectURL(url)
clockSeqNode = BigInt('0x'+id[3]+id[4])
}
else {
const a4 = this.a4; this.getRandomValues(this.a4);
clockSeqNode = (BigInt(a4[2]) << 32n) | BigInt(a4[3])
}
// simulate the 48-bit node-id and 13-bit clock-seq
// to combine with 3-bit uuid-variant
return this.xnode48_ = clockSeqNode & 0xFFFF_FFFF_FFFFn;
}
static get jdNow()/*:<double#ns7>*/{
// return 2440587.5+Date.now()/864e5 // <- Date-quantum-ms form (7ns form below)
return this.jdFromNs7(this.ns7Now)
}
static get ns7Now()/*:<BigInt#60>*/{
if (typeof performance !== 'undefined' && performance.now)
Reflect.defineProperty(this, 'ns7Now',
Reflect.getOwnPropertyDescriptor(this,'ns7Now_performance'))
else
Reflect.defineProperty(this, 'ns7Now',
Reflect.getOwnPropertyDescriptor(this,'ns7Now_Date'))
return this.ns7Now
}
static get ns7Now_Date()/*:<BigInt#60>*/{
// const epoch1582Ns7_bias = 0x1b2_1dd2_1381_4000 // V1 1582 Oct 15
// const epoch1601Ns7_bias = 0x19d_b1de_d53e_8000n // FILETIME base
const epoch1970Ns7 = BigInt(Date.now() * 1000_0.0)
return epoch1970Ns7 + 0x1b2_1dd2_1381_4000n
}
static get ns7Now_performance()/*:<BigInt#60>*/{
const epochPgNs7 = BigInt(performance.now()*/*15*/1000_0.0|/*17*/0)
if (!this.epoch1970PgNs7) // performance.timing.navigationStart
this.epoch1970PgNs7 = this.ns7Now_Date - epochPgNs7
return epochPgNs7 + this.epoch1970PgNs7
}
static dateFromJd(jd) {return new Date((jd - 2440587.5) * 864e5)}
static dateFromNs7(ns7) {
return new Date(Number(ns7 - 0x1b2_1dd2_1381_4000n) / 1000_0.0)}
static jdFromNs7(ns7) { // atomic-clock leap-seconds (ignored)
return 2440587.5 + (Number(ns7 - 0x1b2_1dd2_1381_4000n) / 864e9)
}
static ns7FromJd(jd) {
return BigInt((jd - 2440587.5) * 864e9) + 0x1b2_1dd2_1381_4000n
}
static getRandomValues(va/*:<Uint32Array>*/) {
if (typeof crypto !== 'undefined' && crypto.getRandomValues)
crypto.getRandomValues(va)
else for(let i = 0, n = va.length; i < n; i += 1)
va[i] = Math.random() * 0x1_0000_0000 >>> 0
}
static get a4() {return this.a4_ || (this.a4_ = new Uint32Array(4))}
static ntohl(v)/*:<BigInt>*/{
let r = '0x', sign = 1n, s = BigInt(v).toString(16)
if (s[0] == '-') s = s.substr(1), sign = -1n
for(let i = s.length; i > 0; i -= 2)
r += (i == 1) ? ('0' + s[i-1]) : s[i-2] + s[i-1]
return sign*BigInt(r)
}
static ntohl32(v)/*:<Number>*/{return Number(this.ntohl(v))}
}
Хотя версия 4 uuid определяет в основном случайный uuid, желательно иметь uuid.
реализация, которая может поддерживать некоторые дополнительные характеристики.
uuid(с использованием BigInt)80 locноминальныйclass
с комментариямиuuid с использованием монотонного time в contexttime, а затем context(с использованием uuid вариант-2)timeJavaScript, если таковая имеетсяEdgeS и ESSязык.
Especially suited for database use with facilities like
SqLite.
es6 class для упрощения расширяемости для номинальной работы по расширению
это предоставить другой uuidвариантыtime и связанные с
API библиотеки eswc.
ntohl api для удобства переупорядочивания строковых представлений BigIntXuid
поддерживает guid, uuid и uid(Идентификатор репо git, fossil, SqLite)
представительства, FILETIME и др.
as in:
{1eb4a659-8bdc-4ce0-c002-b1d505d38ea8}
object stores, где используется uuid в качестве primaryKey
становится желанным.
uuidВариант-2 для обратного значения time
LHS в форме структурированный.putefs(Автоматические имена виртуальной файловой системы EdgeS)service-worker и cloud-serverсинхронизировать и дублировать действияХотя кратко, надеюсь, на данный момент этого достаточно; попытайся
И пожалуйста не стесняйтесь комментировать, отправлять отзывы или предложения.
При выпуске в составе веб-клиента EdgeSeswcбиблиотека на github
шаблоны использования indexedDb с efs будут служить примерами его
замыслы дизайна, которые включают повышение эффективности и удобства использования с
Сценарии indexedDb и связанные с PWA sync и replicate.
uuids / secconst start = Xuid.ns7Now
for(let i = 100000; i; i -=1)
Xuid.v4New
const end = Xuid.ns7Now
console.info(`Delta 7ns: ${(end-start)/100000n}`)
Значения Результат: 16..20 => ~ 2 микросекунды => 500 000uuids / sec
Это просто концепция, которую, безусловно, можно улучшить разными способами, но не так медленно, как я думал.
В общем, этот код включает в себя шестнадцатеричную метку времени в миллисекундах (при некотором взломе он дает 12 цифр, поэтому код будет работать даже после 2527-06-24, но не после 5138-11-16), что означает, что его можно сортировать. Это не так уж и случайно, он использует MAC-адрес для последних 12 цифр. 13-я буква жестко закодирована как 1, чтобы ее можно было сортировать.
После этого следующие 6 цифр берутся из полуслучайной строки, где первые цифры берутся из количества записей, сгенерированных за эту миллисекунду, а другие цифры генерируются случайным образом. Эта 6-значная часть содержит тире и жестко закодированную букву «а», чтобы записи можно было сортировать.
Я знаю, что это можно сократить и повысить производительность, но я доволен результатами (кроме MAC-адреса)
currentNanoseconds = () => {
return nodeMode ? process.hrtime.bigint() : BigInt(Date.now() * 1000000);
}
nodeFindMacAddress = () => {
// extract MAC address
const interfaces = require('os').networkInterfaces();
let result = null;
for (index in interfaces) {
let entry = interfaces[index];
entry.forEach(item => {
if (item.mac !== '00:00:00:00:00:00') {
result = '-' + item.mac.replace(/:/g, '');
}
});
}
return result;
}
const nodeMode = typeof(process) !== 'undefined';
let macAddress = nodeMode ? nodeFindMacAddress() : '-a52e99ef5efc';
let startTime = currentNanoseconds();
let uuids = []; // array for storing generated uuids, useful for testing
let currentTime = null; // holds the last value of Date.now(), used as base for generating uuid
let timePart = null; // part of uuid generated from Date.now()
let counter = 0; // used for counting records created at certain millisecond
let lastTime = null; // used for resetting record counter
const limit = 1000000;
for (let testCounter = 0; testCounter < limit; testCounter++) {
let uuid = testMe();
if (nodeMode || testCounter <= 50) {
uuids.push(uuid);
}
}
const timePassed = Number(currentNanoseconds() - startTime);
if (nodeMode) {
const fs = require('fs');
fs.writeFileSync('temp.txt', JSON.stringify(uuids).replace(/,/g, ',\n'));
} else {
console.info(uuids);
}
console.info({
operationsPerSecond: (1000 * limit / timePassed).toString() + 'm',
nanosecondsPerCycle: timePassed / limit,
milliSecondsPassed: timePassed / 1000000,
microSecondsPassed: timePassed / 1000,
nanosecondsPassed: timePassed
});
function testMe() {
currentTime = Date.now();
let uuid = null; // function result
if (currentTime !== lastTime) {
// added a 9 before timestamp, so that hex encoded timestamp is 12 digits long, currently it is 11 digits long, and it will be until 2527-06-24
// console.info(Date.parse("2527-06-24").toString(16).length)
// code will stop working on 5138-11-17, because the timestamp will be 15 digits long, and code only handels upto 14 digit timestamps
// console.info((Date.parse("5138-11-17")).toString().length)
timePart = parseInt(('99999999999999' + currentTime).substr(-14)).toString(16);
timePart = timePart.substr(0, 8) + '-' + timePart.substr(8, 4) + '-1';
counter = 0;
}
randomPart = ('000000' + Math.floor(10 * (counter + Math.random()))).slice(-6);
randomPart = randomPart.substr(0, 3) + '-a' + randomPart.substr(3, 3);
uuid = timePart + randomPart + macAddress;
counter++;
lastTime = currentTime;
return uuid;
}Вот функция, которая генерирует статический UUID из строки или случайного UUID, если строка не указана:
function stringToUUID (str)
{
if (str === undefined || !str.length)
str = "" + Math.random() * new Date().getTime() + Math.random();
let c = 0,
r = "";
for (let i = 0; i < str.length; i++)
c = (c + (str.charCodeAt(i) * (i + 1) - 1)) & 0xfffffffffffff;
str = str.substr(str.length / 2) + c.toString(16) + str.substr(0, str.length / 2);
for(let i = 0, p = c + str.length; i < 32; i++)
{
if (i == 8 || i == 12 || i == 16 || i == 20)
r += "-";
c = p = (str[(i ** i + p + 1) % str.length]).charCodeAt(0) + p + i;
if (i == 12)
c = (c % 5) + 1; //1-5
else if (i == 16)
c = (c % 4) + 8; //8-B
else
c %= 16; //0-F
r += c.toString(16);
}
return r;
}
console.info("Random :", stringToUUID());
console.info("Static [1234]:", stringToUUID("1234")); //29c2c73b-52de-4344-9cf6-e6da61cb8656
console.info("Static [test]:", stringToUUID("test")); //e39092c6-1dbb-3ce0-ad3a-2a41db98778cОднострочное решение с использованием Blobs.
window.URL.createObjectURL(new Blob([])).substring(31);
Значение в конце (31) зависит от длины URL-адреса.
В качестве альтернативы window.URL.createObjectURL(new Blob([])).split('/').pop() будет делать то же самое, не полагаясь на внешние факторы, такие как длина URL.
Самая простая функция для этого:
function createGuid(){
let S4 = () => Math.floor((1+Math.random())*0x10000).toString(16).substring(1);
let guid = `${S4()}${S4()}-${S4()}-${S4()}-${S4()}-${S4()}${S4()}${S4()}`;
return guid.toLowerCase();
}
GUID, представленные в виде строк, имеют длину не менее 36 и не более 38 символов и соответствуют шаблону ^ \ {? [A-zA-Z0-9] {36}? \} $ И, следовательно, всегда имеют формат ascii.