¿Cómo crear GUID / UUID?

4181

Estoy tratando de crear identificadores únicos a nivel mundial en JavaScript. No estoy seguro de qué rutinas están disponibles en todos los navegadores, cuán "aleatorio" y sembrado es el generador de números aleatorios incorporado, etc.

El GUID / UUID debe tener al menos 32 caracteres y debe permanecer en el rango ASCII para evitar problemas al pasarlos.

Jason Cohen
fuente
13
Los GUID cuando se repiten como cadenas son de al menos 36 y no más de 38 caracteres de longitud y coinciden con el patrón ^ \ {? [A-zA-Z0-9] {36}? \} $ Y, por lo tanto, siempre son ascii.
AnthonyWJones
2
David Bau ofrece un generador de números aleatorios mucho mejor y visible en davidbau.com/archives/2010/01/30/ ... Escribí un enfoque ligeramente diferente para generar UUID en blogs.cozi.com/tech/2010/04/generating- uuids-in-javascript.html
George V. Reilly
Es extraño que nadie haya mencionado esto todavía, pero para completar, hay una gran cantidad de generadores guid en npm . Estoy dispuesto a apostar que la mayoría de ellos también funcionan en el navegador.
George Mauer

Respuestas:

2339

Los UUID (identificador único universal), también conocidos como GUID (identificador único global), de acuerdo con RFC 4122 , son identificadores diseñados para proporcionar ciertas garantías de unicidad.

Si bien es posible implementar un UUID que cumpla con RFC en unas pocas líneas de JS (por ejemplo, vea la respuesta de @ broofa , a continuación), existen varias trampas comunes:

  • Formato de identificación no válido (los UUID deben tener la forma " xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx", donde x es uno de [0-9, af] M es uno de [1-5] y N es [8, 9, a o b]
  • Uso de una fuente de aleatoriedad de baja calidad (como Math.random)

Por lo tanto, se alienta a los desarrolladores que escriben código para entornos de producción a utilizar una implementación rigurosa y bien mantenida, como el módulo uuid .

broofa
fuente
186
En realidad, el RFC permite UUID que se crean a partir de números aleatorios. Solo tiene que girar un par de bits para identificarlo como tal. Ver sección 4.4. Algoritmos para crear un UUID a partir de números verdaderamente aleatorios o pseudoaleatorios
Jason DeFontes
Sobre la base de todo en este hilo, creé una versión que es dos veces más rápida que la variante "e7" a continuación, cripto-fuerte, y funciona en todos los principales navegadores y nodos. Es demasiado grande para incluirlo aquí, así que busque una nueva respuesta con mi nombre el 17 de mayo de 2020.
Bennett Barouch
node-uuid ahora se ha convertido en uuid (después de fusionarse con el último proyecto)
Catweazle
4117

Para una solución compatible con RFC4122 versión 4, esta solución de una línea (ish) es la más compacta que se me ocurre:

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

Actualización, 2015-06-02 : Tenga en cuenta que la unicidad UUID depende en gran medida del generador de números aleatorios (RNG) subyacente. La solución anterior utiliza Math.random()por razones de brevedad, sin embargo Math.random()se no garantiza que sea un generador de números aleatorios de alta calidad. Vea el excelente artículo de Adam Hyland en Math.random () para más detalles. Para una solución más robusta, considere usar el módulo uuid , que usa API RNG de mayor calidad.

Actualización, 2015-08-26 : Como nota al margen, este resumen describe cómo determinar cuántas ID se pueden generar antes de alcanzar una cierta probabilidad de colisión. Por ejemplo, con 3.26x10 15 versión 4 RFC4122 UUID tiene una probabilidad de colisión de 1 en un millón.

Actualización, 2017-06-28 : Un buen artículo de desarrolladores de Chrome que discute el estado de la calidad de Math.random PRNG en Chrome, Firefox y Safari. tl; dr: a finales de 2015 es "bastante bueno", pero no de calidad criptográfica. Para abordar ese problema, aquí hay una versión actualizada de la solución anterior que usa ES6, la cryptoAPI y un poco de la magia JS de la que no puedo dar crédito :

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

Actualización, 2020-01-06 : Hay una propuesta en proceso para un uuidmódulo estándar como parte del lenguaje JS

broofa
fuente
30
Publiqué
44
@marc: la calidad de Math.random () es una preocupación. Pero sin un análisis detallado de la implementación subyacente, que casi seguramente varía en el navegador x, no podemos conocer las probabilidades reales de colisión. Entonces, por simplicidad, estoy asumiendo una fuente ideal de aleatoriedad. Pero sí, eso puede ser una suposición peligrosa como lo destaca el problema de muxa. También es por eso que en node- uuid ( github.com/broofa/node-uuid ) prefiero otras API que garanticen una aleatoriedad de calidad criptográfica sobre Math.random (), aunque el rendimiento se vea afectado por ello.
broofa
144
¿Seguramente la respuesta a la pregunta de @ Muxa es 'no'? Nunca es realmente seguro confiar en algo que vino del cliente. Supongo que depende de la probabilidad de que sus usuarios muestren una consola javascript y cambien manualmente la variable a algo que quieran. O simplemente podrían PUBLICAR la identificación que desean. También dependería de si el usuario que elige su propia ID va a causar vulnerabilidades. De cualquier manera, si se trata de una ID de número aleatorio que entra en una tabla, probablemente la estaría generando en el lado del servidor, de modo que sé que tengo control sobre el proceso.
Cam Jackson el
36
@DrewNoakes: los UUID no son solo una cadena de números completamente aleatorios. El "4" es la versión uuid (4 = "aleatorio"). Las marcas "y" donde la variante uuid (diseño de campo, básicamente) necesita ser incorporada Consulte las secciones 4.1.1 y 4.1.3 de ietf.org/rfc/rfc4122.txt para obtener más información.
broofa
55
¿Por qué c== 'x'en lugar de c === 'x'. Porque jshint ha fallado.
Fizer Khan
811

Realmente me gusta cuán limpia es la respuesta de Broofa , pero es lamentable que las implementaciones deficientesMath.random dejen la posibilidad de colisión.

Aquí hay una solución similar que cumple con RFC4122 versión 4 que resuelve ese problema al compensar los primeros 13 números hexadecimales por una porción hexadecimal de la marca de tiempo, y una vez agotadas las compensaciones por una porción hexadecimal de los microsegundos desde la carga de la página. De esa manera, incluso si Math.randomestá en la misma semilla, ambos clientes tendrían que generar el UUID exactamente el mismo número de microsegundos desde la carga de la página (si se admite el tiempo de alto rendimiento) Y exactamente en el mismo milisegundo (o más de 10,000 años después) obtener el mismo 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())


Aquí hay un violín para probar.

Briguy37
fuente
31
Tenga en cuenta new Date().getTime()que no se actualiza cada milisegundo. No estoy seguro de cómo esto afecta la aleatoriedad esperada de su algoritmo.
devios1
84
rendimiento.ahora sería aún mejor. A diferencia de Date.now, las marcas de tiempo devueltas performance.now()no están limitadas a una resolución de un milisegundo. En cambio, representan los tiempos como números de punto flotante con una precisión de hasta microsegundos . Además, a diferencia de Date.now, los valores devueltos por performance.now () siempre aumentan a una velocidad constante , independientemente del reloj del sistema, que puede ajustarse manualmente o sesgarse mediante software como el Protocolo de hora de red.
daniellmb
66
@daniellmb Probablemente debería haber vinculado a MDN u otro para mostrar documentación real y no un polyfill;)
Martin
2
¿Puedo saber de qué sirve el redondeo d = Math.floor(d/16);?
Praveen
2
@Praveen Esa operación desplaza la marca de tiempo un dígito hexadecimal a la derecha y suelta el resto. Su propósito es deshacerse del dígito hexadecimal que acabamos de usar (el menos significativo) y prepararlo para la próxima iteración.
Briguy37
431

La respuesta de broofa es bastante hábil, de hecho, impresionantemente inteligente, realmente ... cumple con rfc4122, algo legible y compacto. ¡Increíble!

Pero si está viendo esa expresión regular, esas muchas replace()devoluciones de llamada, llamadas de función toString()y Math.random()llamadas de función (donde solo usa 4 bits del resultado y desperdicia el resto), puede comenzar a preguntarse sobre el rendimiento. De hecho, joelpt incluso decidió tirar RFC para obtener una velocidad GUID genérica generateQuickGUID.

Pero, ¿podemos obtener velocidad y cumplimiento de RFC? ¡Yo digo si! ¿Podemos mantener la legibilidad? Bueno ... En realidad no, pero es fácil si sigues.

Pero primero, mis resultados, en comparación con broofa, guid(la respuesta aceptada) y el no compatible con 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.

Entonces, en mi sexta iteración de optimizaciones, superé la respuesta más popular en más de 12X , la respuesta aceptada en más de 9X y la respuesta rápida no compatible en 2-3X . Y sigo siendo compatible con rfc4122.

¿Interesado en cómo? Puse la fuente completa en http://jsfiddle.net/jcward/7hyaC/3/ y en http://jsperf.com/uuid-generator-opt/4

Para una explicación, comencemos con el 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 lo tanto, se reemplaza xcon cualquier dígito hexadecimal aleatorio, ycon datos aleatorios (excepto forzar los 2 bits superiores 10según la especificación RFC), y la expresión regular no coincide con los caracteres -o 4, por lo que no tiene que lidiar con ellos. Muy, muy hábil.

Lo primero que debe saber es que las llamadas a funciones son caras, al igual que las expresiones regulares (aunque solo usa 1, tiene 32 devoluciones de llamada, una para cada coincidencia, y en cada una de las 32 devoluciones de llamada llama a Math.random () y v. toString (16)).

El primer paso hacia el rendimiento es eliminar el RegEx y sus funciones de devolución de llamada y utilizar un bucle simple. Esto significa que tenemos que lidiar con los caracteres -y 4mientras que broofa no. Además, tenga en cuenta que podemos usar la indexación de String Array para mantener su elegante arquitectura de plantilla 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())

Básicamente, la misma lógica interna, excepto que verificamos -o 4, y el uso de un ciclo while (en lugar de replace()devoluciones de llamada) nos da una mejora casi 3X.

El siguiente paso es pequeño en el escritorio, pero hace una diferencia decente en el móvil. Hagamos menos llamadas Math.random () y utilicemos todos esos bits aleatorios en lugar de tirar el 87% de ellos con un búfer aleatorio que se desplaza en cada iteración. Muevamos también esa definición de plantilla fuera del ciclo, en caso de que ayude:

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

Esto nos ahorra un 10-30% dependiendo de la plataforma. No está mal. Pero el siguiente gran paso es deshacerse de las llamadas a la función toString con un clásico de optimización: la tabla de búsqueda. Una simple tabla de búsqueda de 16 elementos realizará el trabajo de toString (16) en mucho menos tiempo:

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

La próxima optimización es otro clásico. Dado que solo estamos manejando 4 bits de salida en cada iteración de bucle, reduzcamos a la mitad el número de bucles y procesemos 8 bits en cada iteración. Esto es complicado ya que todavía tenemos que manejar las posiciones de bits compatibles con RFC, pero no es demasiado difícil. Luego tenemos que hacer una tabla de búsqueda más grande (16x16 o 256) para almacenar 0x00 - 0xff, y la compilamos solo una vez, fuera de la función 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())

Probé un e6 () que procesa 16 bits a la vez, todavía usando la LUT de 256 elementos, y mostró los rendimientos decrecientes de la optimización. Aunque tuvo menos iteraciones, la lógica interna se complicó por el mayor procesamiento, y realizó lo mismo en el escritorio, y solo ~ 10% más rápido en dispositivos móviles.

La técnica de optimización final para aplicar: desenrollar el bucle. Como estamos haciendo un bucle un número fijo de veces, técnicamente podemos escribir todo esto a mano. Intenté esto una vez con una sola variable aleatoria r que seguí reasignando, y el rendimiento se derrumbó. Pero con cuatro variables asignadas datos aleatorios por adelantado, luego usando la tabla de búsqueda y aplicando los bits RFC adecuados, esta versión los fuma a todos:

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

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

Lo curioso es que generar 16 bytes de datos aleatorios es la parte fácil. Todo el truco es expresarlo en formato de cadena con cumplimiento de RFC, y se logra con 16 bytes de datos aleatorios, un bucle desenrollado y una tabla de búsqueda.

Espero que mi lógica sea correcta: es muy fácil cometer un error en este tipo de trabajo tedioso. Pero los resultados me parecen buenos. ¡Espero que hayas disfrutado este loco viaje a través de la optimización del código!

Tenga en cuenta: mi objetivo principal era mostrar y enseñar posibles estrategias de optimización. Otras respuestas cubren temas importantes como colisiones y números verdaderamente aleatorios, que son importantes para generar buenos UUID.

Jeff Ward
fuente
14
Este código todavía contiene un par de errores: las Math.random()*0xFFFFFFFFlíneas deben ser Math.random()*0x100000000de aleatoriedad total y >>>0deben usarse en lugar de |0mantener los valores sin signo (aunque con el código actual creo que se salga bien aunque estén firmados). Finalmente, sería una muy buena idea usar estos días window.crypto.getRandomValuessi está disponible, y recurrir a Math.random solo si es absolutamente necesario. Math.random puede tener menos de 128 bits de entropía, en cuyo caso esto sería más vulnerable a colisiones de lo necesario.
Dave
Sobre la base de todo lo que ya está en este hilo, he construido algo dos veces más rápido que "e7", portátil todos los entornos, incluido el nodo, y actualizado de Math.random () a aleatoriedad de fuerza de cifrado. Es posible que no piense que uuid necesita fuerza criptográfica, pero lo que eso significa es aún menos posibilidades de colisión, que es el punto completo de un uuid. Demasiado grande para caber en un comentario, lo publiqué por separado.
Bennett Barouch
164

Aquí hay un código basado en RFC 4122 , sección 4.4 (Algoritmos para crear un UUID a partir de un número verdaderamente aleatorio o pseudoaleatorio).

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
fuente
44
Debe declarar el tamaño de la matriz de antemano en lugar de dimensionarlo dinámicamente a medida que crea el GUID. var s = new Array(36);
MgSam
1
Creo que hay un error muy leve en la línea que establece los bits 6 a 7 de clock_seq_hi_and_reserved en 01. Dado que s [19] es un carácter '0' .. 'f' y no un int 0x0..0xf, (s [19] y 0x3) | 0x8 no se distribuirá aleatoriamente, tenderá a producir más '9 y menos' b '. Esto solo hace una diferencia si te preocupa la distribución aleatoria por alguna razón.
John Velonis
153
let uniqueId = Math.random().toString(36).substring(2) + Date.now().toString(36);

Si los ID se generan con más de 1 milisegundo de diferencia, son 100% únicos.

Si se generan dos ID a intervalos más cortos, y suponiendo que el método aleatorio es verdaderamente aleatorio, esto generaría ID que son 99.99999999999999% de probabilidad de ser globalmente único (colisión en 1 de 10 ^ 15)

Puede aumentar este número agregando más dígitos, pero para generar un 100% de ID únicos, necesitará usar un contador global.

si necesita compatibilidad con RFC, este formato pasará como un GUID válido de la versión 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('-');

Editar: El código anterior sigue la intención, pero no la letra del RFC. Entre otras discrepancias, hay unos pocos dígitos aleatorios cortos. (Agregue más dígitos aleatorios si lo necesita) Lo bueno es que esto es realmente rápido :) Puede probar la validez de su GUID aquí

revs Simon Rigét
fuente
44
¿Pero esto no es UUID?
Marco Kerwitz
No. UUID / GUID es un número de 122 bits (+ seis bits reservados). Puede garantizar la unicidad a través de un servicio de contador global, pero a menudo transmite a tiempo, dirección MAC y aleatoriedad. ¡Los UUID no son aleatorios! El UID que sugiero aquí no está completamente comprimido. Puede comprimirlo, a un entero de 122 bits, agregar los 6 bits predefinidos y los bits aleatorios adicionales (elimine algunos bits del temporizador) y terminará con un UUID / GUID perfectamente formado, que luego deberá convertir a hexadecimal. Para mí, eso realmente no agrega nada más que el cumplimiento de la longitud de la identificación.
Simon Rigét
55
¡Transmitir en direcciones MAC para la unicidad en máquinas virtuales es una mala idea!
Simon Rigét
1
Hago algo como esto, pero con los personajes principales y algunos guiones (por ejemplo, [slug, date, random].join("_")para crear usr_1dcn27itd_hj6onj6phr. Hace que la identificación también se doble como un campo "creado en"
Seph Reed
95

El GUID más rápido como método de generador de cadenas en el formato XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX. Esto no genera GUID compatible con el estándar.

Diez millones de ejecuciones de esta implementación toman solo 32.5 segundos, que es lo más rápido que he visto en un navegador (la única solución sin bucles / iteraciones).

La función es tan simple como:

/**
 * 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 probar el rendimiento, puede ejecutar este código:

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

Estoy seguro de que la mayoría de ustedes comprenderá lo que hice allí, pero tal vez haya al menos una persona que necesite una explicación:

El algoritmo:

  • La Math.random()función devuelve un número decimal entre 0 y 1 con 16 dígitos después del punto de fracción decimal (por ejemplo 0.4363923368509859).
  • Luego tomamos este número y lo convertimos en una cadena con base 16 (del ejemplo anterior lo obtendremos 0.6fb7687f).
    Math.random().toString(16).
  • Luego cortamos el 0.prefijo ( 0.6fb7687f=> 6fb7687f) y obtenemos una cadena con ocho caracteres hexadecimales.
    (Math.random().toString(16).substr(2,8).
  • A veces, la Math.random()función devolverá un número más corto (por ejemplo 0.4363), debido a ceros al final (del ejemplo anterior, en realidad el número es 0.4363000000000000). Es por eso que agrego a esta cadena "000000000"(una cadena con nueve ceros) y luego la corto con la substr()función para que tenga nueve caracteres exactamente (llenando ceros a la derecha).
  • La razón para agregar exactamente nueve ceros se debe al peor de los casos, que es cuando la Math.random()función devolverá exactamente 0 o 1 (probabilidad de 1/10 ^ 16 para cada uno de ellos). Es por eso que necesitábamos agregarle nueve ceros ( "0"+"000000000"o "1"+"000000000"), y luego cortarlo del segundo índice (tercer carácter) con una longitud de ocho caracteres. Para el resto de los casos, la adición de ceros no dañará el resultado porque lo está cortando de todos modos.
    Math.random().toString(16)+"000000000").substr(2,8).

La Asamblea:

  • El GUID está en el siguiente formato XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.
  • Dividí el GUID en 4 piezas, cada pieza dividida en 2 tipos (o formatos): XXXXXXXXy -XXXX-XXXX.
  • Ahora estoy construyendo el GUID utilizando estos 2 tipos de montar el GUID con llamada 4 piezas, de la siguiente manera: XXXXXXXX -XXXX-XXXX -XXXX-XXXX XXXXXXXX.
  • Para diferir entre estos dos tipos, agregué un parámetro de indicador a una función creadora de pares _p8(s), el sparámetro le dice a la función si agregar guiones o no.
  • Finalmente, creamos el GUID con el siguiente encadenamiento: _p8() + _p8(true) + _p8(true) + _p8()y lo devolvemos.

Enlace a esta publicación en mi blog

¡Disfrutar! :-)

Slavik Meltser
fuente
13
Esta implementación es incorrecta. Ciertos caracteres del GUID requieren un tratamiento especial (por ejemplo, el decimotercer dígito debe ser el número 4).
JLRishe
67

Aquí hay una combinación de la respuesta más votada , con una solución alternativa para las colisiones de 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);
        });
    };

En jsbin si quieres probarlo.

ripper234
fuente
3
tenga en cuenta que la primera versión, la única `window.crypto.getRandomValues , does not keep the Version 4 UUIDs format defined by RFC 4122. That is instead of xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx` produce xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
humanityANDpeace
66

Aquí hay una implementación totalmente no conforme pero muy eficaz para generar un identificador único similar a GUID seguro para ASCII.

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

Genera 26 caracteres [a-z0-9], produciendo un UID que es más corto y más exclusivo que los GUID compatibles con RFC. Se pueden agregar guiones trivialmente si la legibilidad humana es importante.

Aquí hay ejemplos de uso y tiempos para esta función y varias de las otras respuestas de esta pregunta. El tiempo se realizó bajo Chrome m25, 10 millones de iteraciones cada uno.

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

Aquí está el código de tiempo.

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

Aquí hay una solución fechada el 9 de octubre de 2011 de un comentario del usuario jed en 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)}

Esto logra el mismo objetivo que la respuesta actual mejor calificada , pero en más de 50 bytes menos al explotar la coerción, la recursión y la notación exponencial. Para aquellos curiosos de cómo funciona, aquí está la forma anotada de una versión anterior de la función:

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

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

Existen otros métodos que implican el uso de un control ActiveX, ¡pero manténgase alejado de estos!

Editar: pensé que valía la pena señalar que ningún generador de GUID puede garantizar claves únicas (consulte el artículo de wikipedia ). Siempre existe la posibilidad de colisiones. Un GUID simplemente ofrece un universo de claves lo suficientemente grande como para reducir el cambio de colisiones a casi nulo.

Prestaul
fuente
8
Tenga en cuenta que esto no es un GUID en el sentido técnico, porque no hace nada para garantizar la unicidad. Eso puede o no importar dependiendo de su aplicación.
Stephen Deken
2
Una nota rápida sobre el rendimiento. Esta solución crea 36 cadenas en total para obtener un solo resultado. Si el rendimiento es crítico, considere crear una matriz y unirse según lo recomendado por: tinyurl.com/y37xtx La investigación adicional indica que puede no importar, por lo que YMMV: tinyurl.com/3l7945
Brandon DuRette
2
Con respecto a la unicidad, vale la pena señalar que la versión 1,3 y 5 UUID son deterministas en formas que la versión 4 no lo es. Si las entradas a estos generadores de uuid (id. De nodo en v1, espacio de nombre y nombre en v3 y v5) son únicas (como se supone que son), entonces los UUID resultantes serán únicos. En teoría, de todos modos.
broofa
41

Puede usar node-uuid ( https://github.com/kelektiv/node-uuid )

Generación simple y rápida de RFC4122 UUIDS.

caracteristicas:

  • Genere RFC4122 versión 1 o versión 4 UUID
  • Se ejecuta en node.js y navegadores.
  • Generación aleatoria # criptográficamente fuerte en plataformas compatibles.
  • Huella pequeña (¿Quieres algo más pequeño? ¡ Mira esto! )

Instalar usando NPM:

npm install uuid

O usando uuid a través del navegador:

Descargar archivo sin formato (uuid v1): https://raw.githubusercontent.com/kelektiv/node-uuid/master/v1.js Descargar archivo sin formato (uuid v4): https://raw.githubusercontent.com/kelektiv/node -uuid / master / v4.js


¿Quieres aún más pequeño? Mira esto: 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
fuente
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:

Revisé mi proyecto que estaba usando esta función y no me gustó la verbosidad. - Pero necesitaba una aleatoriedad adecuada.

Una versión basada en la respuesta de Briguy37 y algunos operadores bit a bit para extraer ventanas de tamaño nibble del búfer.

Debería cumplir con el esquema RFC Tipo 4 (aleatorio), ya que la última vez tuve problemas al analizar los uuids no conformes con el UUID de Java.

insomnio
fuente
31

Módulo JavaScript simple como una combinación de las mejores respuestas en este hilo.

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
fuente
1
Lo que preocupa de todas las respuestas es que parece correcto que JavaScript almacene el archivo GUIDcomo string. Su respuesta al menos aborda el almacenamiento mucho más eficiente utilizando a Uint16Array. La toStringfunción debería usar la representación binaria en un JavaScriptobject
Sebastian
Estos UUID producidos por este código son débiles pero compatibles con RFC (_guid) o fuertes pero no compatibles con RFC (_cryptoGuid). El primero usa Math.random (), que ahora se sabe que es un RNG pobre. Este último no puede establecer la versión y los campos variantes.
broofa
@broofa - ¿Qué sugeriría para que sea fuerte y compatible con RFC? ¿Y por qué _cryptoGuid no es compatible con RFC?
Matt
@Matt _cryptoGuid () establece los 128 bits al azar, lo que significa que no establece la versión y los campos de variantes como se describe en el RFC. Vea mi implementación alternativa de uuidv4 () que usa crypto.getRandomValues ​​() en mi respuesta más votada, arriba, para una implementación fuerte + compatible.
broofa
29

Esto crea UUID versión 4 (creado a partir de números pseudoaleatorios):

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

Aquí hay una muestra de los UUID generados:

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

Bueno, esto ya tiene un montón de respuestas, pero desafortunadamente no hay un "verdadero" azar en el grupo. La versión a continuación es una adaptación de la respuesta de broofa, pero se actualizó para incluir una función aleatoria "verdadera" que utiliza bibliotecas criptográficas cuando están disponibles, y la función Alea () como alternativa.

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

Proyecto JavaScript en GitHub - https://github.com/LiosK/UUID.js

UUID.js El generador de UUID compatible con RFC para JavaScript.

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

Características Genera UUID que cumplen con RFC 4122.

Están disponibles los UUID de la versión 4 (UUID de números aleatorios) y los UUID de la versión 1 (UUID basados ​​en el tiempo).

El objeto UUID permite una variedad de acceso al UUID, incluido el acceso a los campos UUID.

La baja resolución de la marca de tiempo de JavaScript se compensa con números aleatorios.

Wojciech Bednarski
fuente
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
fuente
16

Quería entender la respuesta de broofa, así que la amplié y agregué comentarios:

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
fuente
Gracias por la descripción detallada! Específicamente nibble enjaulado entre 8 y 11 con explicación equivalente es muy útil.
Egor Litvinchuk
15

Ajusté mi propio generador de UUID / GUID con algunos extras aquí .

Estoy usando el siguiente generador de números aleatorios de Kybos para que sea un poco más criptográfico.

A continuación se muestra mi script con los métodos Mash y Kybos de baagoe.com excluidos.

//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));
Rastreador1
fuente
15

Para aquellos que desean una solución compatible con rfc4122 versión 4 con consideraciones de velocidad (pocas llamadas a 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() );

La función anterior debe tener un equilibrio decente entre velocidad y aleatoriedad.

John Fowler
fuente
13

Muestra 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
fuente
12

La mejor manera:

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

Lo sé, es una vieja pregunta. Solo para completar, si su entorno es SharePoint, hay una función de utilidad llamada SP.Guid.newGuid( enlace msdn ) que crea una nueva guía. Esta función está dentro del archivo sp.init.js. Si reescribe esta función (para eliminar algunas otras dependencias de otras funciones privadas), se verá así:

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

Este se basa en la fecha y agrega un sufijo aleatorio para "garantizar" la unicidad. Funciona bien para identificadores CSS. Siempre devuelve algo así y es 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;
        };
abadejo
fuente
11

Código simple que se usa crypto.getRandomValues(a)en los navegadores compatibles (IE11 +, iOS7 +, FF21 +, Chrome, Android Chrome). Evita el uso Math.random()porque eso puede causar colisiones (por ejemplo, 20 colisiones para 4000 líquidos generados en una situación real por 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:

  • Optimizado para la legibilidad del código, no para la velocidad, tan adecuado para unos pocos cientos de uuid por segundo. Genera alrededor de 10000 uuid () por segundo en Chromium en mi computadora portátil usando http://jsbin.com/fuwigo/1 para medir el rendimiento.
  • Solo usa 8 para "y" porque eso simplifica la legibilidad del código (y puede ser 8, 9, A o B).
robocat
fuente
11

Si solo necesita una cadena aleatoria de 128 bits en ningún formato en particular, puede usar:

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

Lo que devolverá algo así 2350143528-4164020887-938913176-2513998651.

Jonathan Potter
fuente
Por cierto, ¿por qué genera solo números y no caracteres también? mucho menos seguro
vsync
1
también puede agregar caracteres (letras) como este:Array.from((window.crypto || window.msCrypto).getRandomValues(new Uint32Array(4))).map(n => n.toString(16)).join('-')
magikMaker
11

Solo otra variante más legible con solo dos mutaciones.

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, '-');
}
cediendo
fuente
Bueno, la mayoría de los desarrolladores de js son desarrolladores web, y no entenderemos qué hacen los operadores bit a bit, porque no los usamos la mayoría de las veces que desarrollamos. En realidad nunca necesité ninguno de ellos, y soy un desarrollador js desde el '97. Por lo tanto, su código de ejemplo sigue siendo totalmente ilegible para el desarrollador web promedio que lo leerá. Sin mencionar que todavía usa nombres de variables de una sola letra, lo que lo hace aún más críptico. Probablemente lea Código limpio, tal vez eso ayude: amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/…
inf3rno
@ inf3rno no lo critique, todas las soluciones propuestas en este hilo son crípticas, pero son respuestas correctas teniendo en cuenta que la pregunta era tener un tipo de respuesta. eso es lo que las frases son crípticas. no pueden permitirse el lujo de ser legibles para el desarrollador promedio, pero guardan el espacio de la pantalla donde lo hará un simple comentario anterior. Y como resultado, termina siendo mucho más legible de esa manera que si hubiera estado en "código legible".
tatsu
Aleatorio! = Único
usuario1529413
@ user1529413 Sí. La unicidad requiere un índice.
ceving
Esta es mi respuesta favorita, porque está construyendo un UUID como un valor de 16 bytes (128 bits), y no su forma serializada, agradable de leer. Sería trivialmente fácil soltar el material de la cadena y simplemente establecer los bits correctos de un 128bit aleatorio, que es todo lo que un uuidv4 debe ser. Puede basarlo en 64 para URL más cortas, devolverlo a algún ensamblaje web, almacenarlo en menos espacio de memoria que como una cadena, convertirlo en un búfer de tamaño 4096 y poner 256 uuids en él, almacenar en un navegador db, etc. Mucho mejor que tener todo como una cadena larga y minúscula codificada en hexadecimal desde el principio.
Josh de Qaribou
8

OK, usando el paquete uuid , es compatible con los UUID de las versiones 1, 3, 4 y 5 :

yarn add uuid

y entonces:

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

También puede hacerlo con opciones completamente 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 obtener más información, visite la página npm aquí.

Alireza
fuente
6

Es importante usar un código bien probado que sea mantenido por más de 1 contribuidor en lugar de azotar sus propias cosas para esto. Este es uno de los lugares donde probablemente desee preferir el código más estable que la versión inteligente más corta posible que funcione en el navegador X pero no tenga en cuenta las idiosincrasias de Y, que a menudo conducirían a errores muy difíciles de investigar que se manifiestan al azar para algunos usuarios Personalmente, uso uuid-js en https://github.com/aurigadl/uuid-js que Bower habilitó para poder tomar actualizaciones fácilmente.

Shital Shah
fuente