Frage GUID / UUID in JavaScript erstellen?


Ich versuche, global eindeutige Bezeichner in JavaScript zu erstellen. Ich bin mir nicht sicher, welche Routinen in allen Browsern verfügbar sind, wie "zufällig" der eingebaute Zufallszahlengenerator gesetzt ist, usw.

Die GUID / UUID sollte mindestens 32 Zeichen lang sein und sollte im ASCII-Bereich bleiben, um Probleme beim Übergeben zu vermeiden.


3207


Ursprung


Antworten:


Es hat ein paar Versuche gegeben. Die Frage ist: Wollen Sie tatsächliche GUIDs oder nur Zufallszahlen? aussehen wie GUIDs? Es ist einfach genug, Zufallszahlen zu generieren.

function guid() {
  function s4() {
    return Math.floor((1 + Math.random()) * 0x10000)
      .toString(16)
      .substring(1);
  }
  return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
}

Beachten Sie jedoch, dass solche Werte sind keine echten GUIDs.

Es gibt keine Möglichkeit, echte GUIDs in Javascript zu generieren, da sie von Eigenschaften des lokalen Computers abhängen, die von Browsern nicht verfügbar gemacht werden. Sie müssen Betriebssystemspezifische Dienste wie ActiveX verwenden: http://p2p.wrox.com/topicindex/20339.htm

Edit: nicht korrekt - RFC4122 erlaubt zufällige ("Version 4") GUIDs. Siehe andere Antworten für Details.

Hinweis: Das mitgelieferte Code-Snippet folgt nicht RFC4122, was erfordert, dass die Version (4) muss in den generierten Ausgabestring integriert werden. Benutze diese Antwort nicht wenn Sie konforme GUIDs benötigen.

Benutzen:

var uuid = guid();

Demo:

function guid() {
  return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
    s4() + '-' + s4() + s4() + s4();
}

function s4() {
  return Math.floor((1 + Math.random()) * 0x10000)
    .toString(16)
    .substring(1);
}

document.getElementById('jsGenId').addEventListener('click', function() {
  document.getElementById('jsIdResult').value = guid();
})
input { font-family: monospace; }
<button id="jsGenId" type="button">Generate GUID</button>
<br>
<input id="jsIdResult" type="text" placeholder="Results will be placed here..." readonly size="40"/>


1895



Für ein RFC4122 Version 4 konforme Lösung, diese One-Liner (ish) Lösung ist die kompakteste, die ich mir vorstellen konnte .:

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

Update, 02.06.2015Beachten Sie, dass die UUID-Eindeutigkeit stark vom zugrunde liegenden Zufallszahlengenerator (RNG) abhängt. Die obige Lösung verwendet Math.random() der Kürze halber Math.random() ist nicht garantiert ein hochwertiger RNG. Sehen Sie Adam Hyland ausgezeichnete Beschreibung auf Math.random () für Details. Betrachten Sie für eine robustere Lösung etwas wie das UUID-Modul[Haftungsausschluss: Ich bin der Autor], die, wo verfügbar, RNG APIs höherer Qualität verwendet.

Update, 2015-08-26: Als eine Randnotiz, das Kern beschreibt, wie ermittelt werden kann, wie viele IDs vor Erreichen einer bestimmten Kollisionswahrscheinlichkeit generiert werden können. Zum Beispiel mit 3,26 x 1015 Version 4 RFC4122 UUIDs haben Sie eine 1-in-einer-Million-Kollisionswahrscheinlichkeit.

Update, 2017-06-28: EIN guter Artikel von Chrome-Entwicklern Erörterung des Status von Math.random PRNG-Qualität in Chrome, Firefox und Safari. tl; dr - Ab Ende 2015 ist es "ziemlich gut", aber nicht kryptographische Qualität. Um dieses Problem zu beheben, ist hier eine aktualisierte Version der obigen Lösung, die ES6 verwendet, die crypto API und Ein bisschen von JS Wizardy kann ich nicht für gut:

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


3141



Ich mag wirklich, wie sauber Broofa's Antwort ist, aber es ist bedauerlich, dass schlechte Implementierungen von Math.random lassen Sie die Chance für eine Kollision.

Hier ist ein ähnliches RFC4122 Version 4 konforme Lösung, die dieses Problem löst, indem die ersten 13 Hex-Zahlen durch einen Hex-Teil des Zeitstempels versetzt werden. Auf diese Weise, selbst wenn Math.randomauf demselben Seed ist, müssten beide Clients die UUID genau zur gleichen Millisekunde (oder 10.000+ Jahre später) generieren, um die gleiche UUID zu erhalten:

function generateUUID() { // Public Domain/MIT
    var d = new Date().getTime();
    if (typeof performance !== 'undefined' && typeof performance.now === 'function'){
        d += performance.now(); //use high-precision timer if available
    }
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
        var r = (d + Math.random() * 16) % 16 | 0;
        d = Math.floor(d / 16);
        return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
    });
}


Hier ist eine Geige zum Testen.


671



Die Antwort von broofa ist ziemlich glatt, tatsächlich - beeindruckend clever, wirklich ... rfc4122-konform, etwas lesbar und kompakt. Genial!

Aber wenn Sie diesen regulären Ausdruck betrachten, diese vielen replace() Rückrufe, toString()und Math.random() Funktionsaufrufe (wo er nur 4 Bits des Ergebnisses verwendet und den Rest verschwendet), können Sie sich über die Leistung wundern. In der Tat hat Joelpt sogar beschlossen, RFC für generische GUID-Geschwindigkeit mit zu werfen generateQuickGUID.

Aber können wir Geschwindigkeit bekommen? und RFC-Konformität? Ich sage ja!  Können wir die Lesbarkeit erhalten? Nun ... nicht wirklich, aber es ist einfach, wenn Sie mitkommen.

Aber zuerst, meine Ergebnisse, im Vergleich zu Brouna, guid (die akzeptierte Antwort), und die nicht-RFC-konform 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 that results will vary by browser/cpu.

Also habe ich bei meiner 6. Iteration von Optimierungen die populärste Antwort mit "over" geschlagen 12X, die akzeptierte Antwort von über 9Xund die schnell-nicht-konforme Antwort von 2-3X. Und ich bin immer noch rfc4122-konform.

Interessiert wie? Ich habe die volle Quelle angegeben http://jsfiddle.net/jcward/7hyaC/3/ und weiter http://jsperf.com/uuid-generator-opt/4

Für eine Erklärung beginnen wir mit dem Code von broofa:

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

So ersetzt es x mit irgendeiner zufälligen hex Ziffer, y mit zufälligen Daten (außer die oberen 2 Bits zu zwingen 10 per RFC-Spezifikation), und die Regex passt nicht zu - oder 4 Charaktere, also muss er nicht mit ihnen umgehen. Sehr, sehr glatt.

Das erste, was zu wissen ist, dass Funktionsaufrufe teuer sind, genauso wie reguläre Ausdrücke (obwohl er nur 1 verwendet, hat er 32 Rückrufe, einen für jede Übereinstimmung und in jedem der 32 Rückrufe Math.random () und v. toString (16)).

Der erste Schritt in Richtung Leistung besteht darin, die RegEx- und Callback-Funktionen zu eliminieren und stattdessen eine einfache Schleife zu verwenden. Das heißt, wir müssen uns mit der - und 4 Charaktere während Broofa nicht. Beachten Sie auch, dass wir String Array-Indizierung verwenden können, um seine schlanke String-Vorlagenarchitektur beizubehalten:

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

Grundsätzlich die gleiche innere Logik, außer dass wir nachsehen - oder 4und mit einer while - Schleife (statt replace() Callbacks) bringt uns eine fast 3-fache Verbesserung!

Der nächste Schritt ist ein kleiner auf dem Desktop, macht aber auf dem Handy einen guten Unterschied. Lassen Sie uns weniger Math.random () aufrufen und all diese zufälligen Bits verwenden, anstatt 87% davon mit einem zufälligen Puffer wegzuwerfen, der bei jeder Iteration verschoben wird. Lassen Sie uns auch diese Template-Definition aus der Schleife entfernen, nur für den Fall, dass es hilft:

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
}

Dies spart je nach Plattform 10-30%. Nicht schlecht. Aber der nächste große Schritt wird die toString Funktionsaufrufe zusammen mit einem Optimierungsklassiker - der Nachschlagetabelle - los. Eine einfache 16-Elemente-Lookup-Tabelle wird die Aufgabe von toString (16) in viel kürzerer Zeit ausführen:

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
}

Die nächste Optimierung ist ein weiterer Klassiker. Da wir in jeder Schleifeniteration nur 4-Bit-Ausgabe verarbeiten, wollen wir die Anzahl der Schleifen halbieren und acht Bits pro Iteration verarbeiten. Dies ist schwierig, da wir immer noch die RFC-konformen Bitpositionen handhaben müssen, aber es ist nicht zu schwer. Wir müssen dann eine größere Nachschlagetabelle (16x16 oder 256) erstellen, um 0x00 - 0xff zu speichern, und wir erstellen sie nur einmal außerhalb der e5 () - Funktion.

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
}

Ich probierte ein e6 (), das 16 Bits gleichzeitig verarbeitet, immer noch die 256-Elemente-LUT verwendend, und es zeigte den abnehmenden Gewinn der Optimierung. Obwohl es weniger Iterationen gab, wurde die innere Logik durch die erhöhte Verarbeitung kompliziert, und sie machte dasselbe auf dem Desktop und nur ~ 10% schneller auf Mobilgeräten.

Die endgültige zu verwendende Optimierungstechnik - wickeln Sie die Schleife ab. Da wir eine feste Anzahl von Wiederholungen durchlaufen, können wir das alles technisch von Hand schreiben. Ich habe das einmal mit einer einzigen Zufallsvariablen versucht, die ich immer wieder neu zugewiesen habe, und die Leistung wurde getankt. Aber mit vier Variablen, die im Voraus Zufallsdaten zugewiesen wurden, dann verwendet diese Version die Lookup-Tabelle und wendet die richtigen RFC-Bits an.

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

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

Die lustige Sache ist, 16 Bytes zufällige Daten zu erzeugen ist der einfache Teil. Der ganze Trick besteht darin, es im String-Format mit RFC-Konformität auszudrücken, und es wird am dichtesten mit 16 Bytes von zufälligen Daten, einer entrollten Schleife und einer Nachschlagetabelle durchgeführt.

Ich hoffe, meine Logik ist richtig - es ist sehr leicht, einen Fehler in dieser Art von langweiliger Bitarbeit zu machen. Aber die Ergebnisse sehen gut aus. Ich hoffe, dir hat diese tolle Fahrt durch Code-Optimierung gefallen!

Beraten werden: Mein primäres Ziel war es, mögliche Optimierungsstrategien zu zeigen und zu lehren. Andere Antworten behandeln wichtige Themen wie Kollisionen und echte Zufallszahlen, die wichtig sind, um gute UUIDs zu erzeugen.


305



Hier ist ein Code basierend auf RFC 4122, Abschnitt 4.4 (Algorithmen zum Erstellen einer UUID aus einer echten zufälligen oder Pseudo-Zufallszahl).

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

136



Schnellste GUID wie String Generator Methode im Format XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX. Dies erzeugt keine standardkonforme GUID.

Zehn Millionen Ausführungen dieser Implementierung benötigen nur 32,5 Sekunden, was die schnellste ist, die ich jemals in einem Browser gesehen habe (die einzige Lösung ohne Schleifen / Iterationen).

Die Funktion ist so einfach wie:

/**
 * Generates a GUID string.
 * @returns {String} The generated GUID.
 * @example af8a8416-6e18-a307-bd9c-f2c947bbb3aa
 * @author Slavik Meltser (slavik@meltser.info).
 * @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();
}

Um die Leistung zu testen, können Sie diesen Code ausführen:

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

Ich bin sicher, die meisten von euch werden verstehen, was ich dort gemacht habe, aber vielleicht gibt es wenigstens eine Person, die eine Erklärung benötigt:

Der Algorithmus:

  • Das Math.random() Funktion gibt eine Dezimalzahl zwischen 0 und 1 mit 16 Nachkommastellen zurück (z Beispiel 0.4363923368509859).
  • Dann nehmen wir diese Zahl und konvertieren es zu einer Zeichenfolge mit Basis 16 (aus dem obigen Beispiel werden wir bekommen 0.6fb7687f).
    Math.random().toString(16).
  • Dann schnitten wir die 0. Präfix (0.6fb7687f => 6fb7687f) und eine Zeichenfolge mit acht Hexadezimalzahlen erhalten Zeichen lang.
    (Math.random().toString(16).substr(2,8).
  • Manchmal Math.random()Funktion wird zurückkehren kürzere Zahl (z. B. 0.4363), aufgrund von Nullen am Ende (aus dem obigen Beispiel ist die Zahl tatsächlich) 0.4363000000000000). Deshalb füge ich diese Zeichenfolge hinzu "000000000" (eine Zeichenfolge mit neun Nullen) und dann mit abgeschnitten substr() Funktion, um es neun Zeichen genau zu machen (Nullen nach rechts füllend).
  • Der Grund für das Hinzufügen von genau neun Nullen ist wegen des Worst-Case-Szenarios, bei dem der Math.random() Funktion wird genau 0 oder 1 zurückgeben (Wahrscheinlichkeit von 1/10 ^ 16 für jede von ihnen). Deshalb mussten wir neun Nullen hinzufügen ("0"+"000000000" oder "1"+"000000000") und dann von dem zweiten Index (drittes Zeichen) mit einer Länge von acht Zeichen abgeschnitten. In den anderen Fällen wird das Hinzufügen von Nullen das Ergebnis nicht beeinträchtigen, da es sowieso abgeschnitten wird.
    Math.random().toString(16)+"000000000").substr(2,8).

Die Versammlung:

  • Die GUID hat das folgende Format XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.
  • Ich habe die GUID in 4 Teile aufgeteilt, jedes Stück in 2 Typen (oder Formate) unterteilt: XXXXXXXX und -XXXX-XXXX.
  • Jetzt baue ich die GUID mit diesen 2 Typen, um die GUID mit Aufruf 4 Stück wie folgt zusammenzustellen: XXXXXXXX  -XXXX-XXXX  -XXXX-XXXX  XXXXXXXX.
  • Um zwischen diesen beiden Typen zu unterscheiden, habe ich einen Flag-Parameter zu einer Paarerstellungsfunktion hinzugefügt _p8(s), das s Parameter teilt der Funktion mit, ob Bindestriche hinzugefügt werden sollen oder nicht.
  • Schließlich erstellen wir die GUID mit der folgenden Verkettung: _p8() + _p8(true) + _p8(true) + _p8()und gib es zurück.

Link zu diesem Post in meinem Blog

Genießen! :-)


78



var uniqueId = Math.random().toString(36).substring(2) 
               + (new Date()).getTime().toString(36);

Wenn IDs mehr als 1 Millisekunde auseinander liegen, sind sie 100% eindeutig.

Wenn zwei IDs in kürzeren Intervallen generiert werden und angenommen wird, dass die Zufallsmethode wirklich zufällig ist, würde dies IDs erzeugen, die 99,999999999999999% wahrscheinlich global einzigartig sind (Kollision in 1 von 10 ^ 15).

Sie können diese Zahl erhöhen, indem Sie mehr Ziffern hinzufügen. Um jedoch 100% eindeutige IDs zu generieren, müssen Sie einen globalen Zähler verwenden.

document.getElementById("unique").innerHTML =
  Math.random().toString(36).substring(2) + (new Date()).getTime().toString(36);
<div id="unique">
</div>


68



Hier ist eine Kombination der oben gewählte Antwort, mit einem Workaround für Chrome-Kollisionen:

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

    :

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

Auf Jsbin wenn du es testen willst.


57



Hier ist eine Lösung vom 9. Oktober 2011 aus einem Kommentar des Benutzers Jed beim 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)}

Dies erreicht das gleiche Ziel wie die aktuell am höchsten bewertete Antwort, aber in 50+ weniger Bytes durch Ausnutzen von Numerierung, Rekursion und Exponentialnotation. Für die Neugierigen, wie es funktioniert, hier ist die annotierte Form einer älteren Version der Funktion:

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

52