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.
javascript
guid
uuid
Jason Cohen
fonte
fonte
Respostas:
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:
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
", onde x é um de [0-9, af] M é um de [1-5] e N é [8, 9, a ou b]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 .
fonte
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 :
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
crypto
API e um pouco de magia JS, da qual não posso acreditar :Atualização, 06-01 2020 : Existe uma proposta em andamento para um
uuid
módulo padrão como parte da linguagem JSfonte
c== 'x'
vez dec === 'x'
. Porque jshint falhou.Eu realmente gosto do quão limpa é a resposta de Broofa , mas é lamentável que más implementações de
Math.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.random
estiver 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:Aqui está um violino para testar.
fonte
new Date().getTime()
não é atualizado a cada milissegundo. Não tenho certeza de como isso afeta a aleatoriedade esperada do seu algoritmo.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.d = Math.floor(d/16);
?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 eMath.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éricagenerateQuickGUID
.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 rfcgenerateQuickGuid
: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:
Por isso, substitui
x
qualquer dígito hexadecimaly
aleató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-
ou4
, 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
-
e4
, 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:Basicamente, a mesma lógica interna, exceto que verificamos
-
ou4
, 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:
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:
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 ().
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:
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.
fonte
Math.random()*0xFFFFFFFF
linhas devem terMath.random()*0x100000000
aleatoriedade total e>>>0
devem ser usadas em vez de|0
manter 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.getRandomValues
se 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.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).
fonte
var s = new Array(36);
Mostrar snippet de código
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:
Mostrar snippet de código
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
fonte
[slug, date, random].join("_")
para criarusr_1dcn27itd_hj6onj6phr
Torna-se assim que a id também funciona como um "criado pelo" campo.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:
Para testar o desempenho, você pode executar este código:
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:
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 exemplo0.4363923368509859
).0.6fb7687f
).Math.random().toString(16)
.0.
prefixo (0.6fb7687f
=>6fb7687f
) e obtemos uma string com oito caracteres hexadecimais.(Math.random().toString(16).substr(2,8)
.Math.random()
função retornará um número menor (por exemplo0.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).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:
XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
.XXXXXXXX
e-XXXX-XXXX
.XXXXXXXX
-XXXX-XXXX
-XXXX-XXXX
XXXXXXXX
._p8(s)
, os
parâmetro informa à função se você deve adicionar traços ou não._p8() + _p8(true) + _p8(true) + _p8()
e retornamos.Link para este post no meu blog
Aproveitar! :-)
fonte
Aqui está uma combinação da resposta mais votada , com uma solução alternativa para as colisões do Chrome :
No jsbin, se você quiser testá-lo.
fonte
, does not keep the Version 4 UUIDs format defined by RFC 4122. That is instead of
xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`, produzxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
.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.
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.
Aqui está o código de temporização.
fonte
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 :
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:
fonte
Do blog técnico de sagi shkedy :
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.
fonte
Você pode usar o node-uuid ( https://github.com/kelektiv/node-uuid )
Geração simples e rápida de UUIDS RFC4122.
Recursos:
Instale usando o NPM:
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:
ES6:
fonte
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.
fonte
Módulo JavaScript simples como uma combinação das melhores respostas neste tópico.
Uso:
fonte
GUID
como astring
. Sua resposta aborda pelo menos o armazenamento muito mais eficiente usando aUint16Array
. AtoString
função deve estar usando a representação binária em um JavaScriptobject
Isso cria a versão 4 UUID (criada a partir de números pseudo-aleatórios):
Aqui está uma amostra dos UUIDs gerados:
fonte
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.
fonte
Projeto JavaScript no GitHub - https://github.com/LiosK/UUID.js
fonte
fonte
Eu queria entender a resposta de broofa, então a expandi e adicionei comentários:
fonte
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.
fonte
Para aqueles que desejam uma solução compatível com rfc4122 versão 4 com considerações de velocidade (poucas chamadas para Math.random ()):
A função acima deve ter um equilíbrio decente entre velocidade e aleatoriedade.
fonte
Amostra ES6
fonte
A melhor maneira:
Minimizado:
fonte
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:fonte
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
fonte
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 ).Notas:
fonte
Se você precisar apenas de uma sequência aleatória de 128 bits em nenhum formato específico, poderá usar:
O que retornará algo como
2350143528-4164020887-938913176-2513998651
.fonte
Array.from((window.crypto || window.msCrypto).getRandomValues(new Uint32Array(4))).map(n => n.toString(16)).join('-')
Apenas outra variante mais legível com apenas duas mutações.
fonte
OK, usando o pacote uuid , ele suporta os UUIDs das versões 1, 3, 4 e 5 :
e depois:
Você também pode fazê-lo com opções totalmente especificadas:
Para mais informações, visite a página npm aqui
fonte
É 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.
fonte