Comment créer GUID / UUID?

4182

J'essaie de créer des identificateurs uniques au monde en JavaScript. Je ne sais pas quelles routines sont disponibles sur tous les navigateurs, à quel point le générateur de nombres aléatoires est "aléatoire" et prédéfini, etc.

Le GUID / UUID doit être d'au moins 32 caractères et doit rester dans la plage ASCII pour éviter les problèmes lors de leur passage.

Jason Cohen
la source
13
Les GUID lorsqu'ils sont représentés sous forme de chaînes ont au moins 36 et pas plus de 38 caractères et correspondent au modèle ^ \ {? [A-zA-Z0-9] {36}? \} $ Et sont donc toujours ascii.
AnthonyWJones
2
David Bau fournit un générateur de nombres aléatoires bien meilleur et semable sur davidbau.com/archives/2010/01/30/… J'ai écrit une approche légèrement différente pour générer des UUID sur blogs.cozi.com/tech/2010/04/generating- uuids-in-javascript.html
George V. Reilly
Bizarre que personne n'ait encore mentionné cela, mais pour être complet, il y a une pléthore de générateurs GUID sur npm, je suis prêt à parier que la plupart d'entre eux fonctionnent également dans le navigateur.
George Mauer

Réponses:

2339

UUID (Universally Unique IDentifier), également connu sous le nom de GUID (Globally Unique IDentifier), selon la RFC 4122 , sont des identifiants conçus pour fournir certaines garanties d'unicité.

Bien qu'il soit possible d'implémenter des UUID conformes à la RFC dans quelques lignes de JS (par exemple, voir la réponse de @ broofa , ci-dessous), il existe plusieurs pièges courants:

  • Format d'ID non valide (les UUID doivent être de la forme " xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx", où x est l'un de [0-9, af] M est l'un de [1-5] et N est [8, 9, a ou b]]
  • Utilisation d'une source aléatoire de faible qualité (telle que Math.random)

Ainsi, les développeurs écrivant du code pour les environnements de production sont encouragés à utiliser une implémentation rigoureuse et bien entretenue telle que le module uuid .

broofa
la source
186
En fait, la RFC autorise les UUID créés à partir de nombres aléatoires. Il suffit de tordre quelques bits pour l'identifier comme tel. Voir rubrique 4.4. Algorithmes pour créer un UUID à partir de nombres vraiment aléatoires ou pseudo-aléatoires: rfc-archive.org/getrfc.php?rfc=4122
Jason DeFontes
En m'appuyant sur tout dans ce fil déjà, j'ai créé une version deux fois plus rapide que la variante "e7" ci-dessous, crypto-forte, et fonctionne sur tous les principaux navigateurs et nœuds. C'est trop grand pour être inclus ici, alors cherchez une nouvelle réponse avec mon nom le 17 mai 2020.
Bennett Barouch
node-uuid est devenu uuid (après avoir fusionné avec ce dernier projet)
Catweazle
4120

Pour une solution compatible RFC4122 version 4, cette solution à une ligne (ish) est la plus compacte que j'ai pu trouver:

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

Mise à jour, 2015-06-02 : sachez que l'unicité des UUID dépend fortement du générateur de nombres aléatoires (RNG) sous-jacent. La solution ci-dessus utilise Math.random()pour la brièveté, cependant, Math.random()n'est pas garanti d'être un RNG de haute qualité. Voir l' excellent ouvrage d' Adam Hyland sur Math.random () pour plus de détails. Pour une solution plus robuste, envisagez d'utiliser le module uuid , qui utilise des API RNG de meilleure qualité.

Mise à jour, 2015-08-26 : En guise de note latérale, cet élément essentiel décrit comment déterminer le nombre d'ID pouvant être générés avant d'atteindre une certaine probabilité de collision. Par exemple, avec les UUID RFC4122 3.26x10 15 version 4, vous avez une chance sur un million de collision.

Mise à jour, 28/06/2017 : Un bon article de développeurs Chrome discutant de l'état de la qualité PRNG Math.random dans Chrome, Firefox et Safari. tl; dr - À la fin de 2015, c'est "assez bien", mais pas la qualité cryptographique. Pour résoudre ce problème, voici une version mise à jour de la solution ci-dessus qui utilise ES6, l' cryptoAPI et un peu de magie JS pour laquelle je ne peux pas m'attribuer le mérite :

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

Mise à jour, 2020-01-06 : Une proposition de norme est en préparationuuid module dans le langage JS

broofa
la source
30
J'ai posté une question sur les collisions stackoverflow.com/questions/6906916/…
Muxa
4
@marc - La qualité de Math.random () est une préoccupation. Mais sans une analyse détaillée de l'implémentation sous-jacente, qui varie presque sûrement x-browser, nous ne pouvons pas connaître les chances réelles de collision. Donc, pour simplifier, je suppose une source aléatoire aléatoire. Mais, oui, cela peut être une hypothèse dangereuse comme le souligne le problème de muxa. C'est aussi pourquoi dans node-uuid ( github.com/broofa/node-uuid ) je préfère d'autres API qui garantissent un caractère aléatoire de qualité cryptographique à Math.random (), même si les performances en souffrent.
broofa
144
La réponse à la question de @ Muxa est sûrement «non»? Il n'est jamais vraiment sûr de faire confiance à quelque chose qui vient du client. Je suppose que cela dépend de la probabilité que vos utilisateurs mettent en place une console javascript et modifient manuellement la variable pour en faire ce qu'ils veulent. Ou ils pourraient simplement vous POSTER en arrière l'identifiant qu'ils veulent. Cela dépendra également si l'utilisateur qui choisit son propre ID va causer des vulnérabilités. Quoi qu'il en soit, si c'est un ID de nombre aléatoire qui entre dans une table, je le générerais probablement côté serveur, afin que je sache que j'ai le contrôle sur le processus.
Cam Jackson
36
@DrewNoakes - Les UUID ne sont pas seulement une chaîne de # complètement aléatoires. Le "4" est la version uuid (4 = "aléatoire"). Le "y" marque l'endroit où la variante uuid (disposition du champ, essentiellement) doit être incorporée. Voir les sections 4.1.1 et 4.1.3 de ietf.org/rfc/rfc4122.txt pour plus d'informations.
broofa
5
pourquoi c== 'x'au lieu de c === 'x'. Parce que jshint a échoué.
Fizer Khan
811

J'aime vraiment la pureté de la réponse de Broofa , mais il est regrettable que les mauvaises implémentations deMath.random laissent la chance de collision.

Voici une solution conforme à la RFC4122 version 4 similaire qui résout ce problème en compensant les 13 premiers nombres hexadécimaux par une partie hexadécimale de l'horodatage, et une fois épuisée compensée par une partie hexadécimale des microsecondes depuis le chargement de page. De cette façon, même s'il Math.randomest sur la même graine, les deux clients devraient générer l'UUID exactement le même nombre de microsecondes depuis le chargement de page (si le temps de haute performance est pris en charge) ET à exactement la même milliseconde (ou plus de 10 000 ans plus tard) pour obtenir le même 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())


Voici un violon à tester.

Briguy37
la source
31
Gardez à l'esprit, new Date().getTime()n'est pas mis à jour toutes les millisecondes. Je ne sais pas comment cela affecte le caractère aléatoire attendu de votre algorithme.
devios1
84
performance.now serait encore mieux. Contrairement à Date.now, les horodatages renvoyés par performance.now()ne sont pas limités à une résolution d'une milliseconde. Au lieu de cela, ils représentent les temps sous forme de nombres à virgule flottante avec une précision allant jusqu'à la microseconde . Contrairement à Date.now, les valeurs renvoyées par performance.now () augmentent toujours à un taux constant , indépendamment de l'horloge système qui peut être ajustée manuellement ou biaisée par un logiciel tel que le Network Time Protocol.
daniellmb
6
@daniellmb Vous auriez probablement dû vous lier à MDN ou à un autre pour afficher une vraie documentation et non un polyfill;)
Martin
2
Puis-je savoir à quoi sert l'arrondi d = Math.floor(d/16);?
Praveen
2
@Praveen Cette opération décale l'horodatage d'un chiffre hexadécimal vers la droite et supprime le reste. Son but est de se débarrasser du chiffre hexadécimal que nous venons d'utiliser (le moins significatif) et de le préparer pour la prochaine itération.
Briguy37
431

La réponse de broofa est assez lisse, en effet - incroyablement intelligent, vraiment ... conforme rfc4122, quelque peu lisible et compact. Impressionnant!

Mais si vous regardez cette expression régulière, ces nombreux replace()rappels toString()et Math.random()appels de fonction (où il n'utilise que 4 bits du résultat et gaspille le reste), vous pouvez commencer à vous interroger sur les performances. En effet, joelpt a même décidé de lancer RFC pour la vitesse GUID générique avecgenerateQuickGUID .

Mais, pouvons-nous obtenir la vitesse et la conformité RFC? Je dis oui! Pouvons-nous maintenir la lisibilité? Eh bien ... Pas vraiment, mais c'est facile si vous suivez.

Mais d'abord, mes résultats, par rapport à broofa, guid(la réponse acceptée) et le non-rfc conforme 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.

Donc, à ma 6e itération d'optimisations, j'ai battu la réponse la plus populaire de plus de 12X , la réponse acceptée de plus de 9X et la réponse rapide non conforme de 2-3X . Et je suis toujours conforme rfc4122.

Intéressé par comment? J'ai mis la source complète sur http://jsfiddle.net/jcward/7hyaC/3/ et sur http://jsperf.com/uuid-generator-opt/4

Pour une explication, commençons par le code 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())

Il remplace donc x par n'importe quel chiffre hexadécimal aléatoire, yavec des données aléatoires (sauf forcer les 2 premiers bits 10selon la spécification RFC), et l'expression régulière ne correspond pas à la -ou4 caractères , il n'a donc pas à les traiter. Très, très lisse.

La première chose à savoir est que les appels de fonction sont chers, tout comme les expressions régulières (bien qu'il n'utilise que 1, il a 32 rappels, un pour chaque correspondance, et dans chacun des 32 rappels, il appelle Math.random () et v. toString (16)).

La première étape vers les performances consiste à éliminer le RegEx et ses fonctions de rappel et à utiliser une boucle simple à la place. Cela signifie que nous devons gérer les caractères -et 4, contrairement à broofa. Notez également que nous pouvons utiliser l'indexation de tableau de chaînes pour conserver son architecture de modèle de chaîne lisse:

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

Fondamentalement, la même logique intérieure, sauf que nous vérifions - ou 4, et en utilisant une boucle while (au lieu dereplace() rappels) nous apporte une amélioration de presque 3 fois!

La prochaine étape est une petite sur le bureau mais fait une différence décente sur mobile. Faisons moins d'appels Math.random () et utilisons tous ces bits aléatoires au lieu de jeter 87% d'entre eux avec un tampon aléatoire qui est décalé à chaque itération. Déplaçons également cette définition de modèle hors de la boucle, juste au cas où cela aiderait:

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

Cela nous permet d'économiser 10-30% selon la plate-forme. Pas mal. Mais la prochaine grande étape se débarrasse des appels de la fonction toString avec un classique d'optimisation - la table de recherche. Une simple table de recherche à 16 éléments effectuera le travail de toString (16) en beaucoup moins de temps:

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 prochaine optimisation est un autre classique. Étant donné que nous ne gérons que 4 bits de sortie à chaque itération de boucle, réduisons le nombre de boucles de moitié et traitons 8 bits à chaque itération. C'est délicat car nous devons encore gérer les positions de bits conformes à la RFC, mais ce n'est pas trop difficile. Nous devons ensuite créer une table de recherche plus grande (16x16 ou 256) pour stocker 0x00 - 0xff, et nous la construisons une seule fois, en dehors de la fonction 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())

J'ai essayé un e6 () qui traite 16 bits à la fois, en utilisant toujours la LUT à 256 éléments, et cela a montré les rendements décroissants de l'optimisation. Bien qu'il ait eu moins d'itérations, la logique interne a été compliquée par l'augmentation du traitement, et il a effectué la même chose sur le bureau, et seulement 10% plus rapide sur le mobile.

La dernière technique d'optimisation à appliquer - déroule la boucle. Puisque nous bouclons un nombre fixe de fois, nous pouvons techniquement tout écrire à la main. J'ai essayé cela une fois avec une seule variable aléatoire r que je continuais de réassigner, et les performances étaient optimisées. Mais avec quatre variables assignées à l'avance des données aléatoires, puis en utilisant la table de recherche et en appliquant les bits RFC appropriés, cette version les fume toutes:

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

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

Le plus drôle, c'est que générer 16 octets de données aléatoires est la partie la plus facile. L'astuce consiste à l'exprimer au format String avec la conformité RFC, et c'est le plus étroitement accompli avec 16 octets de données aléatoires, une boucle déroulée et une table de recherche.

J'espère que ma logique est correcte - il est très facile de se tromper dans ce genre de travail fastidieux. Mais les sorties me semblent bonnes. J'espère que vous avez apprécié cette course folle grâce à l'optimisation du code!

Soyez avisé: mon objectif principal était de montrer et d'enseigner des stratégies d'optimisation potentielles. D'autres réponses couvrent des sujets importants tels que les collisions et les nombres vraiment aléatoires, qui sont importants pour générer de bons UUID.

Jeff Ward
la source
14
Ce code contient toujours quelques erreurs: les Math.random()*0xFFFFFFFFlignes doivent être Math.random()*0x100000000pour un caractère aléatoire complet, et >>>0doivent être utilisées au lieu de |0garder les valeurs non signées (bien qu'avec le code actuel, je pense que ça va bien même si elles sont signées). Enfin, ce serait une très bonne idée d'utiliser ces jours-ci window.crypto.getRandomValuessi disponible, et de ne recourir à Math.random qu'en cas de nécessité absolue. Math.random pourrait bien avoir moins de 128 bits d'entropie, auquel cas cela serait plus vulnérable aux collisions que nécessaire.
Dave
En s'appuyant sur tout ce qui existe déjà sur ce fil, j'ai construit quelque chose deux fois plus vite que "e7", portable tous les environnements, y compris le nœud, et mis à niveau de Math.random () vers l'aléatoire de crypto-force. Vous ne pensez peut-être pas que l'uuid a besoin d'une force cryptographique, mais cela signifie encore moins de risques de collision, ce qui est tout l'intérêt d'un uuid. Trop gros pour tenir dans un commentaire, je l'ai posté séparément.
Bennett Barouch
164

Voici un code basé sur RFC 4122 , section 4.4 (Algorithmes pour créer un UUID à partir d'un nombre vraiment aléatoire ou pseudo-aléatoire).

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
la source
4
Vous devez déclarer la taille du tableau à l'avance plutôt que de la dimensionner dynamiquement lors de la création du GUID. var s = new Array(36);
MgSam
1
Je pense qu'il y a un bug très mineur dans la ligne qui définit les bits bits 6-7 de clock_seq_hi_and_reserved à 01. Puisque s [19] est un caractère '0' .. 'f' et non un entier 0x0..0xf, (s [19] & 0x3) | 0x8 ne sera pas distribué de manière aléatoire - il aura tendance à produire plus de «9» et moins de «b». Cela ne fait une différence que si vous vous souciez de la distribution aléatoire pour une raison quelconque.
John Velonis
153
let uniqueId = Math.random().toString(36).substring(2) + Date.now().toString(36);

Si les ID sont générés à plus d'une milliseconde d'intervalle, ils sont 100% uniques.

Si deux ID sont générés à des intervalles plus courts, et en supposant que la méthode aléatoire est vraiment aléatoire, cela générerait des ID qui sont 99,9999999999999999% susceptibles d'être uniques au monde (collision dans 1 sur 10 ^ 15)

Vous pouvez augmenter ce nombre en ajoutant plus de chiffres, mais pour générer des identifiants 100% uniques, vous devrez utiliser un compteur global.

si vous avez besoin de la compatibilité RFC, cette mise en forme passera comme un GUID valide de la version 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('-');

Modifier: Le code ci-dessus suit l'intention, mais pas la lettre du RFC. Entre autres écarts, il y a quelques chiffres aléatoires courts. (Ajoutez plus de chiffres aléatoires si vous en avez besoin) L'avantage est que c'est très rapide :) Vous pouvez tester la validité de votre GUID ici

Simon Rigét
la source
4
Ce n'est pas UUID cependant?
Marco Kerwitz
Non. Les UUID / GUID sont un nombre de 122 bits (+ six bits réservés). il peut garantir l'unicité grâce à un service de comptage mondial, mais il relaie souvent l'heure, l'adresse MAC et le caractère aléatoire. Les UUID ne sont pas aléatoires! L'UID que je suggère ici n'est pas entièrement compressé. Vous pouvez le compresser, en un entier de 122 bits, ajouter les 6 bits prédéfinis et les bits aléatoires supplémentaires (supprimer quelques bits de minuterie) et vous vous retrouvez avec un UUID / GUID parfaitement formé, que vous devrez ensuite convertir en hexadécimal. Pour moi, cela n'ajoute rien d'autre que la conformité à la longueur de l'ID.
Simon Rigét
5
Relayer les adresses MAC pour unicité sur les machines virtuelles est une mauvaise idée!
Simon Rigét
1
Je fais quelque chose comme ça, mais avec des personnages principaux et quelques tirets (par exemple [slug, date, random].join("_")pour créer usr_1dcn27itd_hj6onj6phr. Cela fait en sorte que l'ID se double également d'un champ "créé à"
Seph Reed
95

GUID le plus rapide comme méthode de générateur de chaînes dans le format XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX. Cela ne génère pas de GUID conforme aux normes.

Dix millions d'exécutions de cette implémentation ne prennent que 32,5 secondes, ce qui est la plus rapide que j'ai jamais vue dans un navigateur (la seule solution sans boucles / itérations).

La fonction est aussi simple que:

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

Pour tester les performances, vous pouvez exécuter ce code:

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

Je suis sûr que la plupart d'entre vous comprendront ce que j'ai fait là-bas, mais il y a peut-être au moins une personne qui aura besoin d'une explication:

L'algorithme:

  • La Math.random()fonction renvoie un nombre décimal compris entre 0 et 1 avec 16 chiffres après le point de fraction décimale (par exemple 0.4363923368509859).
  • Ensuite, nous prenons ce nombre et le convertissons en une chaîne de base 16 (à partir de l'exemple ci-dessus, nous obtiendrons 0.6fb7687f).
    Math.random().toString(16).
  • Ensuite, nous coupons le 0.préfixe ( 0.6fb7687f=> 6fb7687f) et obtenons une chaîne de huit caractères hexadécimaux.
    (Math.random().toString(16).substr(2,8).
  • Parfois, la Math.random()fonction retourne un nombre plus court (par exemple 0.4363), en raison de zéros à la fin (dans l'exemple ci-dessus, en fait, le nombre est 0.4363000000000000). C'est pourquoi j'ajoute à cette chaîne "000000000"(une chaîne avec neuf zéros) puis je la coupe avecsubstr() fonction pour lui donner exactement neuf caractères (en remplissant les zéros à droite).
  • La raison de l'ajout exact de neuf zéros est due au pire des cas, c'est-à-dire lorsque la Math.random()fonction retournera exactement 0 ou 1 (probabilité de 1/10 ^ 16 pour chacun d'eux). C'est pourquoi nous avons dû lui ajouter neuf zéros ( "0"+"000000000"ou "1"+"000000000"), puis le couper du deuxième index (3e caractère) avec une longueur de huit caractères. Pour le reste des cas, l'ajout de zéros ne nuira pas au résultat car il le coupe de toute façon.
    Math.random().toString(16)+"000000000").substr(2,8).

L'Assemblée:

  • Le GUID est au format suivant XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.
  • J'ai divisé le GUID en 4 morceaux, chaque morceau divisé en 2 types (ou formats): XXXXXXXXet -XXXX-XXXX.
  • Maintenant , je vais construire le GUID en utilisant ces 2 types pour assembler le GUID avec des morceaux appel 4, comme suit: XXXXXXXX -XXXX-XXXX -XXXX-XXXX XXXXXXXX.
  • Pour différer entre ces deux types, j'ai ajouté un paramètre de drapeau à une fonction de créateur de paire _p8(s), le sparamètre indique à la fonction s'il faut ajouter des tirets ou non.
  • Finalement, nous construisons le GUID avec le chaînage suivant:, _p8() + _p8(true) + _p8(true) + _p8()et le renvoyons.

Lien vers cet article sur mon blog

Prendre plaisir! :-)

Slavik Meltser
la source
13
Cette implémentation est incorrecte. Certains caractères du GUID nécessitent un traitement spécial (par exemple, le 13e chiffre doit être le chiffre 4).
JLRishe
67

Voici une combinaison de la réponse la plus votée , avec une solution de contournement pour les collisions 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);
        });
    };

Sur jsbin si vous voulez le tester.

ripper234
la source
3
notez que la première version, celle `window.crypto.getRandomValues , does not keep the Version 4 UUIDs format defined by RFC 4122. That is instead of xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`, elle donne xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
humanityANDpeace
66

Voici une implémentation totalement non conforme mais très performante pour générer un identifiant unique de type GUID sécurisé ASCII.

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

Génère 26 [a-z0-9] caractères, produisant un UID qui est à la fois plus court et plus unique que les GUID conformes RFC. Des tirets peuvent être insignifiants si la lisibilité est importante.

Voici des exemples d'utilisation et des délais pour cette fonction et plusieurs autres réponses à cette question. Le chronométrage a été effectué sous Chrome m25, 10 millions d'itérations chacun.

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

Voici le code temporel.

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

Voici une solution datée du 9 octobre 2011 à partir d'un commentaire de l'utilisateur jed sur 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)}

Cela atteint le même objectif que la réponse actuellement la mieux notée , mais dans 50+ octets de moins en exploitant la coercition, la récursivité et la notation exponentielle. Pour les curieux de savoir comment cela fonctionne, voici la forme annotée d'une ancienne version de la fonction:

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
la source
52

Du blog technique 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;
}

Il existe d'autres méthodes qui impliquent l'utilisation d'un contrôle ActiveX, mais restez à l'écart de celles-ci!

Edit: J'ai pensé qu'il valait la peine de souligner qu'aucun générateur GUID ne peut garantir des clés uniques (consultez l' article wikipedia ). Il y a toujours un risque de collision. Un GUID offre simplement un univers de touches suffisamment grand pour réduire le changement de collisions à presque nul.

Prestaul
la source
8
Notez que ce n'est pas un GUID au sens technique, car il ne fait rien pour garantir l'unicité. Cela peut ou non dépendre de votre application.
Stephen Deken
2
Une note rapide sur les performances. Cette solution crée un total de 36 chaînes pour obtenir un résultat unique. Si les performances sont critiques, envisagez de créer un tableau et de vous joindre comme recommandé par: tinyurl.com/y37xtx Des recherches supplémentaires indiquent que cela peut ne pas avoir d'importance, donc YMMV: tinyurl.com/3l7945
Brandon DuRette
2
En ce qui concerne l'unicité, il convient de noter que les UUID des versions 1,3 et 5 sont déterministes, contrairement à la version 4. Si les entrées de ces générateurs uuid - id de nœud dans v1, espace de noms et nom dans v3 et v5 - sont uniques (comme ils sont censés l'être), alors les UUID résultants sont uniques. En théorie, en tout cas.
broofa
41

Vous pouvez utiliser node-uuid ( https://github.com/kelektiv/node-uuid )

Génération simple et rapide d' UUIDS RFC4122.

Fonctionnalités:

  • Générer des UUID RFC4122 version 1 ou version 4
  • Fonctionne dans node.js et les navigateurs.
  • Génération aléatoire cryptographique forte sur les plates-formes de support.
  • Petite empreinte (Vous voulez quelque chose de plus petit? Vérifiez cela! )

Installer à l'aide de NPM:

npm install uuid

Ou en utilisant uuid via un navigateur:

Télécharger le fichier brut (uuid v1): https://raw.githubusercontent.com/kelektiv/node-uuid/master/v1.js Télécharger le fichier brut (uuid v4): https://raw.githubusercontent.com/kelektiv/node -uuid / master / v4.js


Vous voulez encore plus petit? Vérifiez ceci: https://gist.github.com/jed/982883


Usage:

// 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
la source
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);
    });
};

ÉDITER:

Revisité mon projet qui utilisait cette fonction et n'aimait pas la verbosité. - Mais il fallait un caractère aléatoire approprié.

Une version basée sur la réponse de Briguy37 et certains opérateurs au niveau du bit pour extraire les fenêtres de taille quartet du tampon.

Devrait adhérer au schéma RFC Type 4 (aléatoire), car j'ai eu des problèmes lors de la dernière analyse des uuids non conformes avec l'UUID de Java.

sans sommeil
la source
31

Module JavaScript simple comme combinaison des meilleures réponses dans ce fil.

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

Usage:

Guid.newGuid ()

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

Guid.empty

"00000000-0000-0000-0000-000000000000"

kayz1
la source
1
Ce qui dérange dans toutes les réponses, c'est qu'il semble correct pour JavaScript de stocker le fichier en GUIDtant que string. Votre réponse aborde au moins le stockage beaucoup plus efficace en utilisant a Uint16Array. La toStringfonction devrait utiliser la représentation binaire dans un JavaScriptobject
Sebastian
Ces UUID produits par ce code sont soit faibles mais conformes à RFC (_guid), soit forts mais pas conformes à RFC (_cryptoGuid). Le premier utilise Math.random (), qui est maintenant connu pour être un RNG pauvre. Ce dernier ne parvient pas à définir les champs de version et de variante.
broofa
@broofa - Que suggéreriez-vous pour le rendre solide et conforme aux RFC? Et pourquoi _cryptoGuid n'est pas compatible RFC?
Matt
@Matt _cryptoGuid () définit les 128 bits de façon aléatoire, ce qui signifie qu'il ne définit pas les champs de version et de variante comme décrit dans le RFC. Voir mon implémentation alternative de uuidv4 () qui utilise crypto.getRandomValues ​​() dans ma réponse la plus votée, ci-dessus, pour une implémentation forte + conforme.
broofa
29

Cela crée la version 4 UUID (créée à partir de nombres pseudo aléatoires):

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

Voici un échantillon des UUID générés:

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

Eh bien, cela a déjà un tas de réponses, mais malheureusement, il n'y a pas de "vrai" hasard dans le tas. La version ci-dessous est une adaptation de la réponse de broofa, mais mise à jour pour inclure une "vraie" fonction aléatoire qui utilise des bibliothèques de cryptographie lorsqu'elles sont disponibles, et la fonction Alea () comme solution de rechange.

  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
la source
27

Projet JavaScript sur GitHub - https://github.com/LiosK/UUID.js

UUID.js Le générateur UUID compatible RFC pour JavaScript.

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

Caractéristiques Génère des UUID conformes à la RFC 4122.

Les UUID de la version 4 (UUID à partir de nombres aléatoires) et les UUID de la version 1 (UUID temporels) sont disponibles.

L'objet UUID permet une variété d'accès à l'UUID, y compris l'accès aux champs UUID.

La faible résolution d'horodatage de JavaScript est compensée par des nombres aléatoires.

Wojciech Bednarski
la source
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
la source
16

Je voulais comprendre la réponse de broofa, alors je l'ai développée et ajouté des commentaires:

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
la source
Merci pour la description détaillée! Spécifiquement grignoter entre 8 et 11 avec une explication équivalente est super utile.
Egor Litvinchuk
15

Ajustement de mon propre générateur UUID / GUID avec quelques extras ici .

J'utilise le générateur de nombres aléatoires Kybos suivant pour être un peu plus cryptographique.

Ci-dessous mon script avec les méthodes Mash et Kybos de baagoe.com exclues.

//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
la source
15

Pour ceux qui souhaitent une solution compatible rfc4122 version 4 avec des considérations de vitesse (quelques appels à 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 fonction ci-dessus devrait avoir un équilibre décent entre la vitesse et le hasard.

John Fowler
la source
13

Échantillon 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
la source
12

La meilleure façon:

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
 }

Minimisé:

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
la source
11

Je sais, c'est une vieille question. Juste pour être complet, si votre environnement est SharePoint, il existe une fonction utilitaire appelée SP.Guid.newGuid( lien msdn ) qui crée un nouveau guid. Cette fonction se trouve dans le fichier sp.init.js. Si vous réécrivez cette fonction (pour supprimer d'autres dépendances d'autres fonctions privées), cela ressemble à ceci:

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
la source
11

Celui-ci est basé sur la date et ajoute un suffixe aléatoire pour "garantir" l'unicité. Fonctionne bien pour les identificateurs CSS. Il renvoie toujours quelque chose comme et est facile à pirater:

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;
        };
lingue
la source
11

Code simple qui utilise crypto.getRandomValues(a)sur les navigateurs pris en charge (IE11 +, iOS7 +, FF21 +, Chrome, Android Chrome). Évite d'utiliser Math.random()car cela peut provoquer des collisions (par exemple 20 collisions pour 4000 uuids générés en situation réelle par 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);
}

Remarques:

  • Optimisé pour la lisibilité du code et non pour la vitesse, il convient donc à quelques centaines d'uuid par seconde. Génère environ 10000 uuid () par seconde dans Chromium sur mon ordinateur portable en utilisant http://jsbin.com/fuwigo/1 pour mesurer les performances.
  • Utilise uniquement 8 pour "y" car cela simplifie la lisibilité du code (y peut être 8, 9, A ou B).
robocat
la source
11

Si vous avez juste besoin d'une chaîne aléatoire de 128 bits sans format particulier, vous pouvez utiliser:

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

Qui retournera quelque chose comme 2350143528-4164020887-938913176-2513998651.

Jonathan Potter
la source
BTW, pourquoi ne génère-t-il que des chiffres et pas des caractères? beaucoup moins sûr
vsync
1
vous pouvez également ajouter des caractères (lettres) comme celui-ci:Array.from((window.crypto || window.msCrypto).getRandomValues(new Uint32Array(4))).map(n => n.toString(16)).join('-')
magikMaker
11

Juste une autre variante plus lisible avec seulement deux mutations.

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
la source
Eh bien, la plupart des développeurs js sont des développeurs Web, et nous ne comprendrons pas ce que font les opérateurs au niveau du bit, car nous ne les utilisons pas la plupart du temps. En fait, je n'en ai jamais eu besoin, et je suis un développeur js depuis 1997. Ainsi, votre exemple de code est encore totalement illisible pour le développeur Web moyen qui le lira. Sans oublier que vous utilisez toujours des noms de variables à une seule lettre, ce qui le rend encore plus cryptique. Lisez probablement Clean Code, peut-être que cela aide: amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/…
inf3rno
@ inf3rno ne le dénigre pas, toutes les solutions proposées dans ce fil sont cryptiques mais ce sont des réponses correctes étant donné que la question était d'avoir une sorte de doublure. c'est ce que les one-liners sont cryptiques. ils ne peuvent pas se permettre d'être lisibles par le développeur moyen, mais ils économisent de l'espace d'écran où un simple commentaire précédent fera l'affaire. Et en conséquence, finit par être beaucoup plus lisible de cette façon, alors s'il avait été en "code lisible" à la place.
tatsu
Aléatoire! = Unique
user1529413
@ user1529413 Oui. L'unicité nécessite un index.
ceving
C'est ma réponse préférée, car elle crée un UUID en tant que valeur de 16 octets (128 bits), et non sa forme sérialisée et agréable à lire. Il serait trivialement facile de supprimer le contenu de la chaîne et de définir simplement les bons bits d'un 128 bits aléatoire, ce qui est tout ce qu'un uuidv4 doit être. Vous pouvez le baser64 pour des URL plus courtes, le renvoyer à un assemblage Web, le stocker dans moins d'espace mémoire que sous forme de chaîne, en faire un tampon de taille 4096 et y mettre 256 uuids, le stocker dans une base de données de navigateur, etc. Beaucoup mieux que d'avoir tout comme une longue chaîne codée en hexadécimal en minuscules depuis le début.
Josh de Qaribou le
8

OK, en utilisant le package uuid , il prend en charge les UUID des versions 1, 3, 4 et 5 :

yarn add uuid

et alors:

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

Vous pouvez également le faire avec des options entièrement spécifiées:

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'

Pour plus d'informations, visitez la page npm ici

Alireza
la source
6

Il est important d'utiliser du code bien testé qui est maintenu par plus de 1 contributeurs au lieu de fouetter vos propres trucs pour cela. C'est l'un des endroits où vous souhaitez probablement préférer le code le plus stable à la version intelligente la plus courte possible qui fonctionne dans le navigateur X, mais ne tient pas compte des particularités de Y, ce qui entraînerait souvent des enquêtes très difficiles sur les bogues plutôt que de se manifester de manière aléatoire. pour certains utilisateurs. Personnellement, j'utilise uuid-js sur https://github.com/aurigadl/uuid-js qui est activé pour que je puisse prendre des mises à jour facilement.

Shital Shah
la source