Como criar GUID / UUID?

4180

Estou tentando criar identificadores globalmente exclusivos em JavaScript. Não tenho certeza de quais rotinas estão disponíveis em todos os navegadores, de quão "aleatório" e semeado é o gerador de números aleatórios incorporado etc.

O GUID / UUID deve ter pelo menos 32 caracteres e deve permanecer no intervalo ASCII para evitar problemas ao transmiti-los.

Jason Cohen
fonte
13
Os GUIDs, quando representados como strings, têm no mínimo 36 e no máximo 38 caracteres e correspondem ao padrão ^ \ {? [A-zA-Z0-9] {36}? \} $ E, portanto, são sempre ascii.
AnthonyWJones
2
David Bau fornece um gerador de números aleatórios melhorável e inicializável em davidbau.com/archives/2010/01/30/…. Eu escrevi uma abordagem ligeiramente diferente para gerar UUIDs em blogs.cozi.com/tech/2010/04/generating- uuids-in-javascript.html
George V. Reilly
Estranho que ninguém tenha mencionado isso ainda, mas para ser completo, há uma infinidade de geradores de guias na npm . Estou disposto a apostar que a maioria deles também funciona no navegador.
George Mauer

Respostas:

2339

UUIDs (Universally Unique IDentifier), também conhecido como GUIDs (Globally Unique IDentifier), de acordo com a RFC 4122 , são identificadores projetados para fornecer certas garantias de exclusividade.

Embora seja possível implementar UUIDs compatíveis com RFC em algumas linhas de JS (por exemplo, veja a resposta de @ broofa , abaixo), existem várias armadilhas comuns:

  • Formato de identificação inválido (os UUIDs devem ter o formato " xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx", onde x é um de [0-9, af] M é um de [1-5] e N é [8, 9, a ou b]
  • Uso de uma fonte de aleatoriedade de baixa qualidade (como Math.random)

Portanto, os desenvolvedores que escrevem código para ambientes de produção são incentivados a usar uma implementação rigorosa e bem mantida, como o módulo uuid .

broofa
fonte
186
Na verdade, o RFC permite UUIDs criados a partir de números aleatórios. Você só precisa ajustar alguns bits para identificá-lo como tal. Veja a seção 4.4. Algoritmos para criar um UUID a partir de números verdadeiramente aleatórios ou pseudo-aleatórios: rfc-archive.org/getrfc.php?rfc=4122
Jason DeFontes
Com base em tudo nesse segmento, eu criei uma versão duas vezes mais rápida que a variante "e7" abaixo, forte em criptografia e funciona em todos os principais navegadores e nós. É muito grande para ser incluído aqui, então procure uma nova resposta com meu nome em 17 de maio de 2020.
Bennett Barouch
node-uuid agora se tornou uuid (após a fusão com o último projeto)
Catweazle
4115

Para uma solução compatível com RFC4122 versão 4, esta solução de uma linha (ish) é a mais compacta que eu poderia oferecer :

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

Atualização, 02/06/2015 : Esteja ciente de que a exclusividade do UUID depende muito do gerador de números aleatórios (RNG) subjacente. A solução acima usa Math.random()por questões de brevidade, no entanto, nãoMath.random() é garantido que seja um RNG de alta qualidade. Veja o excelente artigo de Adam Hyland em Math.random () para obter detalhes. Para uma solução mais robusta, considere usar o módulo uuid , que usa APIs RNG de qualidade mais alta.

Atualização, 26/08/2015 : Como nota lateral, esta essência descreve como determinar quantos IDs podem ser gerados antes de atingir uma certa probabilidade de colisão. Por exemplo, com 3,26x10 15 versão 4, você tem uma chance de 1 em um milhão de colisão.

Atualização, 28/06/2017 : Um bom artigo dos desenvolvedores do Chrome discutindo o estado da qualidade PRNG do Math.random no Chrome, Firefox e Safari. tl; dr - No final de 2015, é "muito bom", mas não de qualidade criptográfica. Para resolver esse problema, aqui está uma versão atualizada da solução acima que usa o ES6, a cryptoAPI e um pouco de magia JS, da qual não posso acreditar :

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

Atualização, 06-01 2020 : Existe uma proposta em andamento para um uuidmódulo padrão como parte da linguagem JS

broofa
fonte
30
Eu postei uma pergunta sobre colisões stackoverflow.com/questions/6906916/…
Muxa
4
@marc - A qualidade do Math.random () é uma preocupação. Mas, sem uma análise detalhada da implementação subjacente, que quase certamente varia o x-browser, não podemos saber as chances reais de colisão. Por simplicidade, estou assumindo uma fonte ideal de aleatoriedade. Mas, sim, isso pode ser uma suposição perigosa, como destaca a questão de muxa. Também é por isso que no node-uuid ( github.com/broofa/node-uuid ) eu prefiro outras APIs que garantam a aleatoriedade de qualidade criptográfica sobre Math.random (), mesmo que o desempenho seja prejudicado.
broofa 29/07
144
Certamente a resposta para a pergunta de @ Muxa é 'não'? Nunca é realmente seguro confiar em algo que veio do cliente. Eu acho que depende da probabilidade de seus usuários abrirem um console javascript e alterar manualmente a variável para algo que eles desejam. Ou eles podem simplesmente postar de volta o id que eles querem. Também dependeria se o usuário que escolhe seu próprio ID causará vulnerabilidades. De qualquer maneira, se for um ID de número aleatório entrando em uma tabela, eu provavelmente o geraria no lado do servidor, para que eu saiba que tenho controle sobre o processo.
amigos estão dizendo sobre cam
36
@DrewNoakes - os UUIDs não são apenas uma sequência de # completamente aleatórios. O "4" é a versão uuid (4 = "aleatória"). O "y" marca onde a variante uuid (layout do campo, basicamente) precisa ser incorporada. Consulte as seções 4.1.1 e 4.1.3 do ietf.org/rfc/rfc4122.txt para obter mais informações.
broofa
5
porque em c== 'x'vez de c === 'x'. Porque jshint falhou.
Fizer Khan
811

Eu realmente gosto do quão limpa é a resposta de Broofa , mas é lamentável que más implementações deMath.random deixem a chance de colisão.

Aqui está uma solução semelhante à RFC4122 versão 4, que resolve esse problema, compensando os 13 primeiros números hexadecimais por uma parte hexadecimal do carimbo de data e hora e compensando as compensações por uma parte hexadecimal dos microssegundos desde o carregamento de página. Dessa forma, mesmo se Math.randomestiver na mesma semente, os dois clientes teriam que gerar para o UUID exatamente o mesmo número de microssegundos desde o carregamento da página (se houver tempo de alto desempenho) e exatamente no mesmo milissegundo (ou mais de 10.000 anos depois) para obtenha o mesmo UUID:

function generateUUID() { // Public Domain/MIT
    var d = new Date().getTime();//Timestamp
    var d2 = (performance && performance.now && (performance.now()*1000)) || 0;//Time in microseconds since page-load or 0 if unsupported
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = Math.random() * 16;//random number between 0 and 16
        if(d > 0){//Use timestamp until depleted
            r = (d + r)%16 | 0;
            d = Math.floor(d/16);
        } else {//Use microseconds since page-load if supported
            r = (d2 + r)%16 | 0;
            d2 = Math.floor(d2/16);
        }
        return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
    });
}

console.log(generateUUID())


Aqui está um violino para testar.

Briguy37
fonte
31
Lembre-se, new Date().getTime()não é atualizado a cada milissegundo. Não tenho certeza de como isso afeta a aleatoriedade esperada do seu algoritmo.
devios1
84
performance.now seria ainda melhor. Diferentemente de Date.now, os carimbos de data e hora retornados por performance.now()não se limitam à resolução de um milissegundo. Em vez disso, eles representam os tempos como números de ponto flutuante com precisão de até microssegundos . Diferentemente de Date.now, os valores retornados por performance.now () sempre aumentam a uma taxa constante , independentemente do relógio do sistema que pode ser ajustado manualmente ou inclinado por software como o Network Time Protocol.
Daniellmb # 13/14
6
@daniellmb Você provavelmente deveria ter vinculado ao MDN ou outro para mostrar documentação real e não um polyfill;)
Martin
2
Posso saber de que serve o arredondamento d = Math.floor(d/16);?
Praveen
2
@Praveen Essa operação altera o carimbo de data e hora um dígito hexadecimal para a direita e elimina o restante. Seu objetivo é livrar-se do dígito hexadecimal que acabamos de usar (o menos significativo) e prepará-lo para a próxima iteração.
precisa saber é o seguinte
431

a resposta de broofa é bastante lisa, de fato - impressionantemente inteligente, realmente ... compatível com rfc4122, um tanto legível e compacta. Impressionante!

Mas se você está olhando para essa expressão regular, aqueles muitos replace()retornos de chamada, toString()'s e Math.random()chamadas de função (onde ele está apenas usando 4 bits do resultado e desperdiçando o resto), você pode começar a se perguntar sobre o desempenho. De fato, o joelpt até decidiu jogar fora o RFC para obter velocidade GUID genérica generateQuickGUID.

Mas, podemos obter velocidade e conformidade com RFC? Eu digo sim! Podemos manter a legibilidade? Bem ... Na verdade não, mas é fácil se você seguir em frente.

Mas primeiro, meus resultados, comparados com o broofa guid(a resposta aceita) e o que não é compatível com rfc generateQuickGuid:

                  Desktop   Android
           broofa: 1617ms   12869ms
               e1:  636ms    5778ms
               e2:  606ms    4754ms
               e3:  364ms    3003ms
               e4:  329ms    2015ms
               e5:  147ms    1156ms
               e6:  146ms    1035ms
               e7:  105ms     726ms
             guid:  962ms   10762ms
generateQuickGuid:  292ms    2961ms
  - Note: 500k iterations, results will vary by browser/cpu.

Então, na minha sexta iteração de otimizações, superei a resposta mais popular em mais de 12X , a resposta aceita em mais de 9 vezes e a resposta rápida não-conformidade em 2-3 vezes . E ainda sou compatível com rfc4122.

Interessado em como? Coloquei a fonte completa em http://jsfiddle.net/jcward/7hyaC/3/ e em http://jsperf.com/uuid-generator-opt/4

Para uma explicação, vamos começar com o código de broofa:

function broofa() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
        return v.toString(16);
    });
}

console.log(broofa())

Por isso, substitui xqualquer dígito hexadecimal yaleatório por dados aleatórios (exceto forçar os 2 bits principais a10 conforme a especificação RFC), e o regex não corresponde aos caracteres -ou 4, portanto, ele não precisa lidar com eles. Muito, muito liso.

A primeira coisa a saber é que as chamadas de função são caras, assim como as expressões regulares (embora ele use apenas 1, ele possui 32 retornos de chamada, um para cada correspondência e, em cada um dos 32 retornos de chamada, chama Math.random () e v. toString (16)).

O primeiro passo para o desempenho é eliminar o RegEx e suas funções de retorno de chamada e usar um loop simples. Isso significa que temos que lidar com os caracteres -e 4, enquanto broofa não. Além disso, observe que podemos usar a indexação de String Array para manter sua arquitetura de modelo de String:

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

console.log(e1())

Basicamente, a mesma lógica interna, exceto que verificamos -ou 4, e usando um loop while (em vez dereplace() retornos chamada) nos leva a uma melhoria quase 3X!

O próximo passo é pequeno no desktop, mas faz uma diferença decente no celular. Vamos fazer menos chamadas Math.random () e utilizar todos esses bits aleatórios em vez de jogar 87% deles fora com um buffer aleatório que é deslocado a cada iteração. Também vamos mover essa definição de modelo para fora do loop, para o caso de ajudar:

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

console.log(e2())

Isso economiza de 10 a 30%, dependendo da plataforma. Não é ruim. Mas o próximo grande passo elimina completamente as chamadas da função toString com um clássico de otimização - a tabela de consulta. Uma tabela simples de pesquisa de 16 elementos executará o trabalho de toString (16) em muito menos tempo:

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

console.log(e4())

A próxima otimização é outro clássico. Como estamos lidando apenas com 4 bits de saída em cada iteração de loop, vamos cortar o número de loops pela metade e processar 8 bits por iteração. Isso é complicado, pois ainda precisamos lidar com as posições de bits compatíveis com RFC, mas não é muito difícil. Em seguida, precisamos criar uma tabela de pesquisa maior (16x16 ou 256) para armazenar 0x00 - 0xff e construí-la apenas uma vez, fora da função e5 ().

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

console.log(e5())

Tentei um e6 () que processa 16 bits por vez, ainda usando o LUT de 256 elementos, e mostrava os retornos decrescentes de otimização. Embora tivesse menos iterações, a lógica interna era complicada pelo aumento do processamento, e executava o mesmo no desktop e apenas 10% mais rápido no celular.

A técnica final de otimização a ser aplicada - desenrole o loop. Como estamos repetindo um número fixo de vezes, tecnicamente podemos escrever tudo isso manualmente. Eu tentei isso uma vez com uma única variável aleatória r que continuei atribuindo novamente e o desempenho foi prejudicado. Porém, com quatro variáveis ​​atribuídas a dados aleatórios antecipadamente, usando a tabela de pesquisa e aplicando os bits RFC apropriados, esta versão fuma todos eles:

var lut = []; for (var i=0; i<256; i++) { lut[i] = (i<16?'0':'')+(i).toString(16); }
function e7()
{
    var d0 = Math.random()*0xffffffff|0;
    var d1 = Math.random()*0xffffffff|0;
    var d2 = Math.random()*0xffffffff|0;
    var d3 = Math.random()*0xffffffff|0;
    return lut[d0&0xff]+lut[d0>>8&0xff]+lut[d0>>16&0xff]+lut[d0>>24&0xff]+'-'+
    lut[d1&0xff]+lut[d1>>8&0xff]+'-'+lut[d1>>16&0x0f|0x40]+lut[d1>>24&0xff]+'-'+
    lut[d2&0x3f|0x80]+lut[d2>>8&0xff]+'-'+lut[d2>>16&0xff]+lut[d2>>24&0xff]+
    lut[d3&0xff]+lut[d3>>8&0xff]+lut[d3>>16&0xff]+lut[d3>>24&0xff];
}

console.log(e7())

Modificado: http://jcward.com/UUID.js -UUID.generate()

O engraçado é que gerar 16 bytes de dados aleatórios é a parte mais fácil. O truque é expressá-lo no formato String com conformidade com RFC, e é realizado com mais precisão com 16 bytes de dados aleatórios, um loop não rotulado e uma tabela de pesquisa.

Espero que minha lógica esteja correta - é muito fácil cometer um erro nesse tipo de trabalho tedioso. Mas as saídas parecem boas para mim. Espero que você tenha gostado desse passeio louco pela otimização de código!

Esteja ciente: meu principal objetivo era mostrar e ensinar possíveis estratégias de otimização. Outras respostas abrangem tópicos importantes, como colisões e números verdadeiramente aleatórios, importantes para gerar bons UUIDs.

Jeff Ward
fonte
14
Esse código ainda contém alguns erros: as Math.random()*0xFFFFFFFFlinhas devem ter Math.random()*0x100000000aleatoriedade total e >>>0devem ser usadas em vez de |0manter os valores não assinados (embora, com o código atual, eu acho que ele se afasta bem, mesmo que estejam assinados). Finalmente, seria uma boa idéia hoje em dia usá-lo, window.crypto.getRandomValuesse disponível, e retornar ao Math.random apenas se for absolutamente necessário. O Math.random pode ter menos de 128 bits de entropia; nesse caso, isso seria mais vulnerável a colisões do que o necessário.
21415 Dave
Com base em tudo o que já existe neste segmento, criei algo duas vezes mais rápido que o "e7", portável em todos os ambientes, incluindo o nó, e atualizei de Math.random () para aleatoriedade de força de criptografia. Você pode achar que o uuid não precisa de força criptográfica, mas o que isso significa é ainda menos chance de colisão, que é o ponto inteiro de um uuid. Grande demais para caber em um comentário, eu o publiquei separadamente.
Bennett Barouch
164

Aqui está um código baseado no RFC 4122 , seção 4.4 (Algoritmos para criar um UUID a partir de um número verdadeiramente aleatório ou pseudo-aleatório).

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;
}
Kevin Hakanson
fonte
4
Você deve declarar o tamanho da matriz antecipadamente, em vez de dimensioná-lo dinamicamente à medida que cria o GUID. var s = new Array(36);
MgSam
1
Eu acho que há um bug muito menor na linha que define os bits 6-7 do clock_seq_hi_and_reserved para 01. Como s [19] é um caractere '0' .. 'f' e não um int 0x0..0xf, (s [19] e 0x3) | 0x8 não será distribuído aleatoriamente - tenderá a produzir mais '9s e menos' b's. Isso só faz diferença se você se preocupa com a distribuição aleatória por algum motivo.
21413 John Velonis
153
let uniqueId = Math.random().toString(36).substring(2) + Date.now().toString(36);

Se os IDs forem gerados com mais de 1 milissegundo de distância, eles serão 100% exclusivos.

Se dois IDs forem gerados em intervalos mais curtos, e assumindo que o método aleatório seja verdadeiramente aleatório, isso geraria IDs com 99,999999999999999% de probabilidade de serem globalmente únicos (colisão em 1 de 10 ^ 15)

Você pode aumentar esse número adicionando mais dígitos, mas para gerar IDs 100% exclusivos, será necessário usar um contador global.

se você precisar de compatibilidade com RFC, essa formatação passará como um GUID da versão 4 válido:

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

Edit: O código acima segue a intenção, mas não a letra da RFC. Entre outras discrepâncias, há alguns dígitos aleatórios curtos. (Adicione mais dígitos aleatórios, se necessário) A vantagem é que isso é muito rápido :) Você pode testar a validade do seu GUID aqui

Simon Rigét
fonte
4
Este não é UUID embora?
Marco Kerwitz
Não. UUID / GUID é um número de 122 bits (+ seis bits reservados). ele pode garantir exclusividade por meio de um serviço de contador global, mas geralmente é transmitido no horário, endereço MAC e aleatoriedade. UUID's não são aleatórios! O UID que sugiro aqui não está totalmente compactado. Você pode compactá-lo, para um número inteiro de 122 bits, adicionar os 6 bits predefinidos e os bits aleatórios extras (remover alguns bits do timer) e acabar com um UUID / GUID perfeitamente formado, que seria necessário converter em hexadecimal. Para mim, isso realmente não acrescenta nada além de conformidade ao comprimento do ID.
Simon Rigét 18/02/19
5
Retransmitir nos endereços MAC a exclusividade das máquinas virtuais é uma má ideia!
Simon Rigét
1
I fazer algo assim, mas com os principais personagens e alguns traços (por exemplo, [slug, date, random].join("_")para criar usr_1dcn27itd_hj6onj6phrTorna-se assim que a id também funciona como um "criado pelo" campo.
Seph Reed
95

O GUID mais rápido, como o método gerador de string, no formato XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX. Isso não gera GUID compatível com o padrão.

Dez milhões de execuções dessa implementação levam apenas 32,5 segundos, o que é o mais rápido que eu já vi em um navegador (a única solução sem loops / iterações).

A função é tão simples quanto:

/**
 * 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();
}

Para testar o desempenho, você pode executar este código:

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

Tenho certeza de que a maioria de vocês entenderá o que eu fiz lá, mas talvez haja pelo menos uma pessoa que precise de uma explicação:

O algoritmo:

  • A Math.random()função retorna um número decimal entre 0 e 1 com 16 dígitos após o ponto de fração decimal (por exemplo 0.4363923368509859).
  • Então pegamos esse número e o convertemos em uma string com a base 16 (do exemplo acima, obteremos 0.6fb7687f).
    Math.random().toString(16).
  • Em seguida, cortamos o 0.prefixo ( 0.6fb7687f=> 6fb7687f) e obtemos uma string com oito caracteres hexadecimais.
    (Math.random().toString(16).substr(2,8).
  • Às vezes, a Math.random()função retornará um número menor (por exemplo 0.4363), devido a zeros no final (do exemplo acima, na verdade o número é 0.4363000000000000). É por isso que estou anexando a esta string "000000000"(uma string com nove zeros) e cortando-a comsubstr() função para fazer exatamente nove caracteres (preenchendo zeros à direita).
  • O motivo para adicionar exatamente nove zeros é o pior cenário, quando a Math.random()função retornará exatamente 0 ou 1 (probabilidade de 1/10 ^ 16 para cada um deles). É por isso que precisamos adicionar nove zeros a ele ( "0"+"000000000"ou "1"+"000000000") e depois cortá-lo do segundo índice (terceiro caractere) com oito caracteres. Nos demais casos, a adição de zeros não prejudicará o resultado, pois ele o cortará de qualquer maneira.
    Math.random().toString(16)+"000000000").substr(2,8).

A montagem:

  • O GUID está no seguinte formato XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.
  • Dividi o GUID em 4 partes, cada peça dividida em 2 tipos (ou formatos): XXXXXXXXe -XXXX-XXXX.
  • Agora eu estou construindo o GUID usando estes 2 tipos de montar o GUID com a chamada 4 peças, como segue: XXXXXXXX -XXXX-XXXX -XXXX-XXXX XXXXXXXX.
  • Para diferir entre esses dois tipos, adicionei um parâmetro de flag a uma função de criador de pares _p8(s), o sparâmetro informa à função se você deve adicionar traços ou não.
  • Eventualmente, criamos o GUID com o seguinte encadeamento: _p8() + _p8(true) + _p8(true) + _p8()e retornamos.

Link para este post no meu blog

Aproveitar! :-)

Slavik Meltser
fonte
13
Esta implementação está incorreta. Certos caracteres do GUID requerem tratamento especial (por exemplo, o 13º dígito precisa ser o número 4).
JLRishe
67

Aqui está uma combinação da resposta mais votada , com uma solução alternativa para as colisões do Chrome :

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

    :

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

No jsbin, se você quiser testá-lo.

ripper234
fonte
3
note que a primeira versão, aquela `window.crypto.getRandomValues , does not keep the Version 4 UUIDs format defined by RFC 4122. That is instead of xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`, produz xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
HumanidadeANDpeace #
66

Aqui está uma implementação totalmente não compatível, mas de alto desempenho, para gerar um identificador exclusivo do tipo GUID, seguro para ASCII.

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

Gera 26 caracteres [a-z0-9], produzindo um UID mais curto e mais exclusivo que os GUIDs compatíveis com RFC. Os traços podem ser adicionados trivialmente se a legibilidade humana for importante.

Aqui estão exemplos de uso e horários para esta função e várias outras respostas dessa pergunta. O tempo foi realizado no Chrome m25, 10 milhões de iterações cada.

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

Aqui está o código de temporização.

var r;
console.time('t'); 
for (var i = 0; i < 10000000; i++) { 
    r = FuncToTest(); 
};
console.timeEnd('t');
joelpt
fonte
62

Aqui está uma solução datada de 9 de outubro de 2011 de um comentário do usuário jed em 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)}

Isso atinge o mesmo objetivo da resposta atual com a melhor classificação , mas em mais de 50 bytes a menos, explorando a coerção, a recursão e a notação exponencial. Para os curiosos de como funciona, aqui está a forma anotada de uma versão mais antiga da função:

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
      )
}
Jed Schmidt
fonte
52

Do blog técnico de sagi shkedy :

function generateGuid() {
  var result, i, j;
  result = '';
  for(j=0; j<32; j++) {
    if( j == 8 || j == 12 || j == 16 || j == 20) 
      result = result + '-';
    i = Math.floor(Math.random()*16).toString(16).toUpperCase();
    result = result + i;
  }
  return result;
}

Existem outros métodos que envolvem o uso de um controle ActiveX, mas fique longe deles!

Edit: Eu pensei que valia a pena ressaltar que nenhum gerador GUID pode garantir chaves únicas (consulte o artigo da wikipedia ). Sempre há uma chance de colisões. Um GUID simplesmente oferece um universo suficientemente grande de chaves para reduzir a mudança de colisões para quase zero.

Prestaul
fonte
8
Observe que este não é um GUID no sentido técnico, porque não faz nada para garantir a exclusividade. Isso pode ou não importar, dependendo da sua aplicação.
Stephen Deken
2
Uma observação rápida sobre desempenho. Esta solução cria um total de 36 strings para obter um único resultado. Se o desempenho é crítico, considerar a criação de um array e juntando-se como recomendado por: tinyurl.com/y37xtx Mais pesquisas indica que ele não pode importar, então YMMV: tinyurl.com/3l7945
Brandon Durette
2
Em relação à exclusividade, vale a pena notar que os UUIDs da versão 1,3 e 5 são determinísticos da maneira que a versão 4 não é. Se as entradas para esses geradores de uuid - ID do nó na v1, namespace e nome nas v3 e v5 - são únicas (como deveriam ser), os UUIDs resultantes são únicos. Em teoria, pelo menos.
broofa
41

Você pode usar o node-uuid ( https://github.com/kelektiv/node-uuid )

Geração simples e rápida de UUIDS RFC4122.

Recursos:

  • Gere RFC4122 UUIDs versão 1 ou versão 4
  • Executa no node.js e navegadores.
  • Geração de # aleatória criptograficamente forte em plataformas de suporte.
  • Tamanho reduzido (quer algo menor? Confira isso! )

Instale usando o NPM:

npm install uuid

Ou Usando o uuid via navegador:

Baixar arquivo bruto (uuid v1): https://raw.githubusercontent.com/kelektiv/node-uuid/master/v1.js Baixar arquivo cru (uuid v4): https://raw.githubusercontent.com/kelektiv/node -uuid / master / v4.js


Quer ainda menor? Verifique isso: https://gist.github.com/jed/982883


Uso:

// 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();
Kyros Koh
fonte
34
var uuid = function() {
    var buf = new Uint32Array(4);
    window.crypto.getRandomValues(buf);
    var idx = -1;
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        idx++;
        var r = (buf[idx>>3] >> ((idx%8)*4))&15;
        var v = c == 'x' ? r : (r&0x3|0x8);
        return v.toString(16);
    });
};

EDITAR:

Revisitei meu projeto que estava usando essa função e não gostei da verbosidade. - Mas precisava de aleatoriedade adequada.

Uma versão baseada na resposta de Briguy37 e em alguns operadores bit a bit para extrair janelas do tamanho de um nibble do buffer.

Deveria aderir ao esquema RFC tipo 4 (aleatório), já que tive problemas pela última vez ao analisar uuids não compatíveis com o UUID do Java.

sem sono
fonte
31

Módulo JavaScript simples como uma combinação das melhores respostas neste tópico.

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

Uso:

Guid.newGuid ()

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

Guid.empty

"00000000-0000-0000-0000-000000000000"

kayz1
fonte
1
O que incomoda todas as respostas é que parece aceitável que o JavaScript armazene o arquivo GUIDcomo a string. Sua resposta aborda pelo menos o armazenamento muito mais eficiente usando a Uint16Array. A toStringfunção deve estar usando a representação binária em um JavaScriptobject
Sebastian
Esses UUIDs produzidos por este código são compatíveis com fraco-mas-RFC (_guid) ou fortes-mas-não-compatível com RFC (_cryptoGuid). O primeiro usa Math.random (), que agora é conhecido por ser um RNG ruim. O último está falhando ao definir os campos versão e variante.
broofa
@broofa - O que você sugere para torná-lo forte e compatível com RFC? E por que _cryptoGuid não é compatível com RFC?
Matt
O @Matt _cryptoGuid () define todos os 128 bits aleatoriamente, o que significa que não define os campos de versão e variante conforme descrito na RFC. Veja minha implementação alternativa de uuidv4 () que usa crypto.getRandomValues ​​() na minha resposta mais votada, acima, para uma implementação forte + compatível.
broofa 16/03/19
29

Isso cria a versão 4 UUID (criada a partir de números pseudo-aleatórios):

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

Aqui está uma amostra dos UUIDs gerados:

682db637-0f31-4847-9cdf-25ba9613a75c
97d19478-3ab2-4aa1-b8cc-a1c3540f54aa
2eed04c9-2692-456d-a0fd-51012f947136
Mathieu Pagé
fonte
28

Bem, isso já tem várias respostas, mas infelizmente não existe uma aleatória "verdadeira" no grupo. A versão abaixo é uma adaptação da resposta de broofa, mas atualizada para incluir uma função aleatória "verdadeira" que usa bibliotecas de criptografia quando disponíveis, e a função Alea () como fallback.

  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);
  });
};
jvenema
fonte
27

Projeto JavaScript no GitHub - https://github.com/LiosK/UUID.js

UUID.js O gerador de UUID compatível com RFC para JavaScript.

Veja RFC 4122 http://www.ietf.org/rfc/rfc4122.txt .

Recursos Gera UUIDs compatíveis com RFC 4122.

Os UUIDs da versão 4 (UUIDs de números aleatórios) e os UUIDs da versão 1 (UUIDs baseados em tempo) estão disponíveis.

O objeto UUID permite uma variedade de acesso ao UUID, incluindo o acesso aos campos UUID.

A baixa resolução do carimbo de data e hora do JavaScript é compensada por números aleatórios.

Wojciech Bednarski
fonte
21
  // RFC 4122
  //
  // A UUID is 128 bits long
  //
  // String representation is five fields of 4, 2, 2, 2, and 6 bytes.
  // Fields represented as lowercase, zero-filled, hexadecimal strings, and
  // are separated by dash characters
  //
  // A version 4 UUID is generated by setting all but six bits to randomly
  // chosen values
  var uuid = [
    Math.random().toString(16).slice(2, 10),
    Math.random().toString(16).slice(2, 6),

    // Set the four most significant bits (bits 12 through 15) of the
    // time_hi_and_version field to the 4-bit version number from Section
    // 4.1.3
    (Math.random() * .0625 /* 0x.1 */ + .25 /* 0x.4 */).toString(16).slice(2, 6),

    // Set the two most significant bits (bits 6 and 7) of the
    // clock_seq_hi_and_reserved to zero and one, respectively
    (Math.random() * .25 /* 0x.4 */ + .5 /* 0x.8 */).toString(16).slice(2, 6),

    Math.random().toString(16).slice(2, 14)].join('-');
jablko
fonte
16

Eu queria entender a resposta de broofa, então a expandi e adicionei comentários:

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);
        }
    );
};
Andrew
fonte
Obrigado pela descrição detalhada! Especificamente mordiscar enjaulado entre 8 e 11 com explicação equivalente é super útil.
Egor Litvinchuk
15

Ajustei meu próprio gerador de UUID / GUID com alguns extras aqui .

Estou usando o seguinte Kybos gerador de números aleatórios da para ter um som mais criptográfico.

Abaixo está o meu script com os métodos Mash e Kybos do baagoe.com excluídos.

//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));
Tracker1
fonte
15

Para aqueles que desejam uma solução compatível com rfc4122 versão 4 com considerações de velocidade (poucas chamadas para Math.random ()):

var rand = Math.random;

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

console.log( UUID() );

A função acima deve ter um equilíbrio decente entre velocidade e aleatoriedade.

John Fowler
fonte
13

Amostra ES6

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

A melhor maneira:

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
 }

Minimizado:

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}
Andrea Turri
fonte
11

Eu sei, é uma pergunta antiga. Apenas para completar, se o seu ambiente for o SharePoint, há uma função de utilitário chamada SP.Guid.newGuid( link msdn ) que cria um novo guia. Esta função está dentro do arquivo sp.init.js. Se você reescrever esta função (para remover algumas outras dependências de outras funções particulares), ela se parece com isso:

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;
};
Anatoly Mironov
fonte
11

Este é baseado na data e adicione um sufixo aleatório para "garantir" a exclusividade. Funciona bem para identificadores css. Ele sempre retorna algo como e é fácil de hackear:

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;
        };
ling
fonte
11

Código simples usado crypto.getRandomValues(a)em navegadores compatíveis (IE11 +, iOS7 +, FF21 +, Chrome, Android Chrome). Evita o usoMath.random() porque isso pode causar colisões (por exemplo, 20 colisões para 4000 uuids gerados em uma situação real pelo 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);
}

Notas:

  • Otimizado para facilitar a leitura do código e não a velocidade, é adequado para, por exemplo, algumas centenas de uuids por segundo. Gera cerca de 10000 uuid () por segundo no Chromium no meu laptop usando http://jsbin.com/fuwigo/1 para medir o desempenho.
  • Apenas usa 8 para "y" porque isso simplifica a legibilidade do código (é permitido que y seja 8, 9, A ou B).
robocat
fonte
11

Se você precisar apenas de uma sequência aleatória de 128 bits em nenhum formato específico, poderá usar:

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

O que retornará algo como 2350143528-4164020887-938913176-2513998651.

Jonathan Potter
fonte
BTW, por que gera apenas números e não caracteres também? muito menos seguro
vsync
1
você também pode adicionar caracteres (letras) como este:Array.from((window.crypto || window.msCrypto).getRandomValues(new Uint32Array(4))).map(n => n.toString(16)).join('-')
magikMaker 29/03
11

Apenas outra variante mais legível com apenas duas mutações.

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, '-');
}
ceving
fonte
Bem, a maioria dos js devs são desenvolvedores da Web e não entenderemos o que os operadores bit a bit fazem, porque não os usamos na maioria das vezes que desenvolvemos. Na verdade, nunca precisei de nenhum deles, e sou js dev desde 97. Portanto, seu código de exemplo ainda é totalmente ilegível para o desenvolvedor da Web comum que o lerá. Sem mencionar que você ainda usa nomes de variáveis ​​de letra única, o que a torna ainda mais enigmática. Provavelmente ler Código Limpo, talvez isso ajuda: amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/...
inf3rno
@ inf3rno não bata nele, todas as soluções propostas neste segmento são enigmáticas, mas são respostas corretas, considerando que a pergunta era ter um tipo de uma linha. é isso que as falas são enigmáticas. eles não podem se dar ao luxo de serem legíveis para o desenvolvedor médio, mas economizam o espaço da tela, onde um simples comentário anterior servirá. E, como resultado, acaba sendo muito mais legível dessa maneira do que se tivesse sido em "código legível".
tatsu 6/12/19
Aleatório = única!
user1529413
@ user1529413 Sim. Exclusividade requer um índice.
ceving 14/02
Esta é a minha resposta favorita, porque está criando um UUID como um valor de 16 bytes (128 bits), e não sua forma serializada e agradável de ler. Seria trivialmente fácil descartar as coisas das strings e definir os bits corretos de um 128bit aleatório, que é tudo que um uuidv4 precisa ter. Você poderia base64-lo para URLs mais curtos, passá-lo de volta para alguma montagem na Web, armazená-lo em menos espaço de memória do que como uma string, torná-lo um buffer de tamanho 4096 e colocar 256 uuids nele, armazenar em um navegador db, etc. Muito melhor do que ter tudo como uma string longa e minúscula codificada em hexadecimal desde o início.
Josh de Qaribou
8

OK, usando o pacote uuid , ele suporta os UUIDs das versões 1, 3, 4 e 5 :

yarn add uuid

e depois:

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

Você também pode fazê-lo com opções totalmente especificadas:

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'

Para mais informações, visite a página npm aqui

Alireza
fonte
6

É importante usar código bem testado, mantido por mais de 1 colaboradores, em vez de chicotear suas próprias coisas para isso. Este é um dos lugares em que você provavelmente deseja preferir o código mais estável do que a versão inteligente mais curta possível, que funciona no navegador X, mas não leva em consideração as idiossincrasias de Y, o que muitas vezes levaria a erros de investigação muito difíceis do que os que se manifestam apenas aleatoriamente para alguns usuários. Pessoalmente, uso o uuid-js em https://github.com/aurigadl/uuid-js, com o bower ativado, para que eu possa receber atualizações facilmente.

Shital Shah
fonte