Jak utworzyć identyfikator GUID / UUID?


4173

Próbuję utworzyć unikalne globalnie identyfikatory w JavaScript. Nie jestem pewien, jakie procedury są dostępne we wszystkich przeglądarkach, jak „losowy” i zaszczepiony jest wbudowany generator liczb losowych itp.

Identyfikator GUID / UUID powinien mieć co najmniej 32 znaki i powinien pozostawać w zakresie ASCII, aby uniknąć problemów z ich przekazywaniem.


13
Identyfikatory GUID, gdy są ponownie przedstawiane jako ciągi, mają co najmniej 36 i nie więcej niż 38 znaków długości i pasują do wzorca ^ \ {? [A-zA-Z0-9] {36}? \} $, A zatem są zawsze ascii.
AnthonyWJones

2
David Bau zapewnia znacznie lepszy, generowalny generator liczb losowych na stronie davidbau.com/archives/2010/01/30/…. Napisałem nieco inne podejście do generowania UUID na blogs.cozi.com/tech/2010/04/generating- uuids-in-javascript.html
George V. Reilly

Dziwne, że nikt jeszcze o tym nie wspominał, ale dla kompletności istnieje mnóstwo generatorów GUID na npm. Jestem skłonny założyć się, że większość z nich działa również w przeglądarce.
George Mauer

Odpowiedzi:


2336

Identyfikatory UUID (Universally Unique IDentifier), znane również jako GUID (Globally Unique IDentifier), zgodnie z RFC 4122 , są identyfikatorami zaprojektowanymi w celu zapewnienia pewnych gwarancji unikalności.

Chociaż możliwe jest zaimplementowanie UUID zgodnych z RFC w kilku liniach JS (np. Patrz odpowiedź @ broofa poniżej), istnieje kilka typowych pułapek:

  • Niepoprawny format identyfikatora (identyfikatory UUID muszą mieć postać „ xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx”, gdzie x oznacza jeden z [0–9, af] M oznacza jeden z [1-5], a N oznacza [8, 9, a lub b]
  • Zastosowanie niskiej jakości źródła losowości (np. Math.random)

Dlatego programiści piszący kod dla środowisk produkcyjnych są zachęcani do stosowania rygorystycznej, dobrze utrzymanej implementacji, takiej jak moduł uuid .


186
W rzeczywistości RFC dopuszcza identyfikatory UUID tworzone z liczb losowych. Musisz tylko przekręcić kilka bitów, aby zidentyfikować to jako takie. Patrz punkt 4.4. Algorytmy tworzenia identyfikatora UUID z naprawdę przypadkowych lub pseudolosowych
Jason DeFontes 19.08.08

Opierając się już na wszystkim w tym wątku, stworzyłem wersję, która jest dwa razy szybsza niż wariant „e7” poniżej, silnie kryptograficzny i działa na wszystkich głównych przeglądarkach i węzłach. Jest za duży, by go tu umieścić, więc poszukaj nowej odpowiedzi z moim imieniem 17 maja 2020 r.
Bennett Barouch

node-Uuid stał się teraz UUID (po połączeniu z ostatnim projektem)
Catweazle

4106

W przypadku rozwiązania zgodnego z RFC4122 w wersji 4 to jedno-liniowe (ish) rozwiązanie jest najbardziej kompaktowe, jakie mogłem wymyślić:

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

Aktualizacja, 2015-06-02 : Należy pamiętać, że unikalność UUID w dużej mierze zależy od generatora liczb losowych (RNG). Powyższe rozwiązanie wykorzystuje Math.random()zwięzłość, jednak nieMath.random() ma gwarancji, że będzie wysokiej jakości RNG. Szczegółowe informacje można znaleźć w znakomitym piśmie Adama Hylanda w Math.random () . Aby uzyskać bardziej niezawodne rozwiązanie, rozważ użycie modułu uuid , który używa interfejsów API RNG wyższej jakości.

Aktualizacja, 26.08.2015 : Na marginesie, ta lista opisuje, jak ustalić, ile identyfikatorów można wygenerować przed osiągnięciem pewnego prawdopodobieństwa kolizji. Na przykład w przypadku UUID RFC4122 w wersji 3.26x10 15 w wersji 4 masz szansę na kolizję 1 na milion.

Aktualizacja, 28.06.2017 : Dobry artykuł od twórców Chrome omawiający stan jakości Math.random PRNG w Chrome, Firefox i Safari. tl; dr - Na koniec 2015 r. jest „całkiem niezły”, ale nie kryptograficzny. Aby rozwiązać ten problem, oto zaktualizowana wersja powyższego rozwiązania, która używa ES6, cryptointerfejsu API i odrobiny czarodziejstwa JS, za które nie mogę liczyć :

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

Aktualizacja, 2020-01-06 : W pracach jest propozycja standardowego uuidmodułu jako części języka JS


30
Zadałem pytanie dotyczące kolizji stackoverflow.com/questions/6906916/...
Muxa

4
@marc - Jakość Math.random () stanowi problem. Ale bez szczegółowej analizy implementacji, która prawie na pewno zmienia przeglądarkę X, nie jesteśmy w stanie poznać rzeczywistych szans na kolizję. Dla uproszczenia zakładam idealne źródło losowości. Ale tak, może to być niebezpieczne założenie, jak podkreśla problem muxa. Dlatego też w node-uuid ( github.com/broofa/node-uuid ) wolę inne interfejsy API, które gwarantują losowość jakości kryptograficznej niż Math.random (), nawet jeśli ma to wpływ na wydajność.
broofa

144
Z pewnością odpowiedź na pytanie @ Muxa brzmi „nie”? Zaufanie do czegoś, co pochodzi od klienta, nigdy nie jest naprawdę bezpieczne. Wydaje mi się, że zależy to od tego, jak prawdopodobne jest, że Twoi użytkownicy uruchomią konsolę javascript i ręcznie zmienną zmienną tak, aby chcieli. Lub mogą po prostu WYŚLIĆ ci identyfikator, którego chcą. Zależy to również od tego, czy użytkownik wybierający własny identyfikator spowoduje luki w zabezpieczeniach. Tak czy inaczej, jeśli jest to identyfikator losowy, który trafia do tabeli, prawdopodobnie wygenerowałbym go po stronie serwera, aby wiedzieć, że mam kontrolę nad procesem.
Cam Jackson,

36
@DrewNoakes - UUID to nie tylko ciąg całkowicie losowych #. „4” to wersja uuid (4 = „losowa”). Znaki „y”, w których należy osadzić wariant UUID (zasadniczo układ pola). Aby uzyskać więcej informacji, zobacz sekcje 4.1.1 i 4.1.3 ietf.org/rfc/rfc4122.txt .
Broofa

5
dlaczego c== 'x'zamiast c === 'x'. Ponieważ jshint nie powiodło się.
Fizer Khan

809

Bardzo podoba mi się, jak czysta jest odpowiedź Broofa , ale niefortunne jest to, że słabe implementacjeMath.random pozostawiają szansę na kolizję.

Oto podobne rozwiązanie zgodne z RFC4122 w wersji 4, które rozwiązuje ten problem poprzez przesunięcie pierwszych 13 liczb szesnastkowych o część szesnastkową znacznika czasu, a po wyczerpaniu przesunięcie o część szesnastkową mikrosekund od załadowania strony. W ten sposób, nawet jeśli Math.randomznajduje się na tym samym ziarnie, obaj klienci musieliby wygenerować UUID dokładnie taką samą liczbę mikrosekund od czasu ładowania strony (jeśli obsługiwany jest czas wysokiej wydajności) ORAZ dokładnie w tej samej milisekundie (lub ponad 10 000 lat później), aby uzyskaj ten sam 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())


Oto skrzypce do przetestowania.


31
Pamiętaj, że new Date().getTime()nie jest aktualizowany co milisekundę. Nie jestem pewien, jak to wpływa na oczekiwaną losowość algorytmu.
devios1

84
performance.now byłoby jeszcze lepsze. W przeciwieństwie do Date.now, znaczniki czasu zwracane przez performance.now()nie są ograniczone do rozdzielczości jednej milisekundy. Zamiast tego reprezentują czasy jako liczby zmiennoprzecinkowe z dokładnością do mikrosekundy . Również w przeciwieństwie do Date.now, wartości zwracane przez performance.now () zawsze rosną ze stałą szybkością , niezależnie od zegara systemowego, który może być regulowany ręcznie lub przekrzywiony przez oprogramowanie takie jak Network Time Protocol.
daniellmb

6
@daniellmb Prawdopodobnie powinieneś był połączyć się z MDN lub inną, aby pokazać prawdziwą dokumentację, a nie polifill;)
Martin

2
Czy mogę wiedzieć, do czego służy zaokrąglanie d = Math.floor(d/16);?
Praveen

2
@Praveen Ta operacja przesuwa znacznik czasu o jedną cyfrę szesnastkową w prawo i upuszcza resztę. Jego celem jest pozbycie się właśnie użytej cyfry szesnastkowej (najmniej znaczącej) i przygotowanie jej do następnej iteracji.
Briguy37,

430

Odpowiedź Broroofa jest naprawdę sprytna - imponująco sprytna, naprawdę ... zgodna z RFC4122, nieco czytelna i kompaktowa. Niesamowite!

Ale jeśli patrzysz na to wyrażenie regularne, te wiele replace()wywołań zwrotnych, wywołań funkcji toString()i Math.random()funkcji (gdzie używa tylko 4 bitów wyniku i marnuje resztę), możesz zacząć zastanawiać się nad wydajnością. Rzeczywiście, joelpt postanowił nawet wyrzucić RFC dla ogólnej prędkości GUID generateQuickGUID.

Ale czy możemy uzyskać szybkość i zgodność z RFC? Powiedziałem tak! Czy możemy zachować czytelność? Cóż ... Niezupełnie, ale łatwo jest postępować zgodnie z nimi.

Ale najpierw moje wyniki, w porównaniu do Broofa guid(zaakceptowana odpowiedź) i niezgodne z 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.

Więc moim 6 iteracji optymalizacji, pobiłem najpopularniejszą odpowiedź o ponad 12X , przyjętym Odpowiedź na 9X , a odpowiedź szybko niezgodnego przez 2-3x . I nadal jestem zgodny z RFC4122.

W jaki sposób? Umieściłem pełne źródło na http://jsfiddle.net/jcward/7hyaC/3/ i na http://jsperf.com/uuid-generator-opt/4

Aby uzyskać wyjaśnienie, zacznijmy od kodu 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())

Zastępuje więc xdowolną losową cyfrą szesnastkową, ylosowymi danymi (z wyjątkiem wymuszania 2 najwyższych bitów 10zgodnie ze specyfikacją RFC), a wyrażenie regularne nie pasuje do znaków -lub 4, więc nie musi się z nimi obchodzić. Bardzo, bardzo zręczny.

Pierwszą rzeczą, którą należy wiedzieć, jest to, że wywołania funkcji są drogie, podobnie jak wyrażenia regularne (chociaż używa tylko 1, ma 32 wywołania zwrotne, po jednym dla każdego dopasowania, a w każdym z 32 wywołań wywołuje Math.random () i v. toString (16)).

Pierwszym krokiem w kierunku wydajności jest wyeliminowanie RegEx i jego funkcji zwrotnych oraz użycie prostej pętli. Oznacza to, że mamy do czynienia z postaciami -i, 4podczas gdy Broofa nie. Zauważ też, że możemy użyć indeksowania tablicy ciągów, aby zachować jego elegancką architekturę szablonów ciągów:

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

Zasadniczo ta sama wewnętrzna logika, z wyjątkiem tego, że sprawdzamy -lub 4, a użycie pętli while (zamiast replace()wywołań zwrotnych) daje nam prawie trzykrotną poprawę!

Kolejny krok jest niewielki na komputerze, ale robi znaczną różnicę na urządzeniach mobilnych. Wykonajmy mniej wywołań Math.random () i wykorzystajmy wszystkie losowe bity zamiast wyrzucać 87% z nich losowym buforem, który jest przesuwany z każdą iteracją. Przenieśmy też tę definicję szablonu z pętli, na wypadek, gdyby pomogła:

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

To oszczędza nam 10-30% w zależności od platformy. Nie jest zły. Ale kolejny duży krok pozbywa się wywołań funkcji toString wraz z klasycznym optymalizatorem - tabelą przeglądową. Prosta 16-elementowa tabela odnośników wykona zadanie toString (16) w znacznie krótszym czasie:

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

Kolejna optymalizacja to kolejny klasyk. Ponieważ obsługujemy tylko 4 bity wyjścia w każdej iteracji pętli, zmniejszmy liczbę pętli o połowę i przetwarzamy 8 bitów w każdej iteracji. Jest to trudne, ponieważ wciąż musimy obsługiwać pozycje bitów zgodne z RFC, ale nie jest to zbyt trudne. Następnie musimy stworzyć większą tabelę wyszukiwania (16 x 16 lub 256) do przechowywania 0x00 - 0xff i budujemy ją tylko raz, poza funkcją 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())

Próbowałem e6 (), który przetwarza 16-bitów jednocześnie, wciąż używając 256-elementowej LUT, i wykazywał malejące zwroty z optymalizacji. Chociaż miało mniej iteracji, wewnętrzna logika była skomplikowana przez zwiększone przetwarzanie i działała tak samo na komputerze stacjonarnym, a tylko ~ 10% szybciej na telefonie komórkowym.

Ostateczna technika optymalizacji do zastosowania - rozwiń pętlę. Ponieważ zapętlamy określoną liczbę razy, technicznie możemy to wszystko zapisać ręcznie. Próbowałem tego raz z jedną losową zmienną r, którą ciągle zmieniałem, a wydajność była pełna. Ale z czterema zmiennymi przypisanymi losowymi danymi z góry, a następnie za pomocą tabeli odnośników i zastosowania odpowiednich bitów RFC, ta wersja pali je wszystkie:

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

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

Zabawne jest to, że generowanie 16 bajtów losowych danych jest łatwą częścią. Cała sztuczka polega na wyrażaniu tego w formacie String z zachowaniem zgodności z RFC, a najsilniej osiąga się to dzięki 16 bajtom losowych danych, rozwijanej pętli i tabeli odnośników.

Mam nadzieję, że moja logika jest poprawna - bardzo łatwo jest popełnić błąd w tego rodzaju żmudnej pracy. Ale wyniki wyglądają dobrze dla mnie. Mam nadzieję, że podobała Ci się ta szalona jazda dzięki optymalizacji kodu!

Pamiętaj: moim głównym celem było pokazanie i nauczenie potencjalnych strategii optymalizacji. Inne odpowiedzi obejmują ważne tematy, takie jak kolizje i naprawdę losowe liczby, które są ważne dla generowania dobrych UUID.


14
Ten kod wciąż zawiera kilka błędów: Math.random()*0xFFFFFFFFwiersze powinny być Math.random()*0x100000000dla pełnej losowości i >>>0powinny być używane zamiast |0utrzymywania wartości bez znaku (choć w obecnym kodzie myślę, że to wychodzi OK, mimo że są podpisane). Wreszcie, byłoby bardzo dobrym pomysłem w tych dniach, aby użyć, window.crypto.getRandomValuesjeśli są dostępne, i powrócić do Math.random tylko wtedy, gdy jest to absolutnie konieczne. Math.random może mieć mniej niż 128 bitów entropii, w takim przypadku byłoby to bardziej podatne na zderzenia niż to konieczne.
Dave

Opierając się na wszystkim, co już jest w tym wątku, zbudowałem coś dwa razy szybciej niż „e7”, przenośnie we wszystkich środowiskach, w tym w węźle, i zaktualizowałem z Math.random () do losowości o sile kryptograficznej. Być może nie uważasz, że Uuid potrzebuje siły kryptograficznej, ale to oznacza jeszcze mniejszą szansę na kolizję, co stanowi cały sens UUID. Zbyt duży, aby zmieścił się w komentarzu, opublikowałem go osobno.
Bennett Barouch

163

Oto kod oparty na RFC 4122 , sekcja 4.4 (Algorytmy tworzenia UUID z naprawdę losowej lub pseudolosowej liczby).

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

4
Powinieneś wcześniej zadeklarować rozmiar tablicy, a nie zmieniać jej dynamicznie podczas tworzenia GUID. var s = new Array(36);
MgSam,

1
Myślę, że w linii jest bardzo drobny błąd, który ustawia bity 6-7 clock_seq_hi_and_reserved na 01. Ponieważ s [19] jest znakiem „0” .. „f”, a nie int 0x0..0xf, (s [19] i 0x3) | 0x8 nie będzie losowo dystrybuowane - będzie generowało więcej „9” i mniej „b”. To robi różnicę, jeśli z jakiegoś powodu zależy ci na losowym rozkładzie.
John Velonis,

151
let uniqueId = Math.random().toString(36).substring(2) + Date.now().toString(36);

Jeśli identyfikatory są generowane w odległości większej niż 1 milisekunda od siebie, są one w 100% unikalne.

Jeśli dwa identyfikatory są generowane w krótszych odstępach czasu i przy założeniu, że metoda losowa jest naprawdę losowa, wygenerowałoby to identyfikatory, które na 99.9999999999999999% są globalnie unikalne (kolizja w 1 z 10 ^ 15)

Możesz zwiększyć tę liczbę, dodając więcej cyfr, ale aby wygenerować 100% unikatowych identyfikatorów, musisz użyć licznika globalnego.

jeśli potrzebujesz zgodności z RFC, to formatowanie przejdzie jako prawidłowy identyfikator GUID wersji 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('-');

Edycja: Powyższy kod jest zgodny z intencją, ale nie jest literą RFC. Wśród innych rozbieżności jest kilka krótkich cyfr losowych. (W razie potrzeby dodaj więcej losowych cyfr) Plusem jest to, że jest to naprawdę szybkie :) Tutaj możesz przetestować ważność swojego identyfikatora GUID


4
To nie jest UUID?
Marco Kerwitz,

Nie. UUID / GUID to liczba 122-bitowa (+ sześć zarezerwowanych bitów). może gwarantować wyjątkowość dzięki globalnej usłudze liczników, ale często zmienia czas, adres MAC i losowość. UUID nie są losowe! UID, który tu sugeruję, nie jest w pełni skompresowany. Możesz go skompresować do 122-bitowej liczby całkowitej, dodać 6 predefiniowanych bitów i dodatkowe losowe bity (usuń kilka bitów timera), a skończysz na doskonale utworzonym UUID / GUID, który następnie będziesz musiał przekonwertować na hex. Dla mnie to tak naprawdę nie dodaje niczego poza zgodnością z długością identyfikatora.
Simon Rigét

5
Przekazywanie adresów MAC dla unikalności na maszynach wirtualnych to zły pomysł!
Simon Rigét

1
Robię coś takiego, ale z wiodącymi postaciami i niektórymi myślnikami (np. [slug, date, random].join("_")Do tworzenia usr_1dcn27itd_hj6onj6phr. To sprawia, że ​​id również podwaja się jako pole „utworzone w”
Seph Reed

95

Najszybsza metoda GUID podobna do generatora łańcuchów w formacie XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX. Nie generuje GUID zgodnego ze standardami.

Dziesięć milionów wykonań tej implementacji zajmuje zaledwie 32,5 sekundy, co jest najszybszym, jakie kiedykolwiek widziałem w przeglądarce (jedyne rozwiązanie bez pętli / iteracji).

Funkcja jest tak prosta, jak:

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

Aby przetestować wydajność, możesz uruchomić ten kod:

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

Jestem pewien, że większość z was zrozumie, co tam zrobiłem, ale może jest co najmniej jedna osoba, która będzie potrzebowała wyjaśnienia:

Algorytm:

  • Math.random()Zwraca liczbę dziesiętną wynoszącą od 0 do 1 z 16 cyfr po ułamek dziesiętny (na przykład 0.4363923368509859).
  • Następnie bierzemy tę liczbę i przekształcamy ją w ciąg znaków o podstawie 16 (z powyższego przykładu otrzymamy 0.6fb7687f).
    Math.random().toString(16).
  • Następnie odcinamy 0.prefiks ( 0.6fb7687f=> 6fb7687f) i otrzymujemy ciąg o długości ośmiu znaków szesnastkowych.
    (Math.random().toString(16).substr(2,8).
  • Czasami Math.random()funkcja zwraca krótszą liczbę (na przykład 0.4363), z powodu zer na końcu (z powyższego przykładu tak naprawdę jest to liczba 0.4363000000000000). Dlatego dołączam do tego ciągu "000000000"(ciąg z dziewięcioma zerami), a następnie odcinam go substr()funkcją, aby dokładnie dziewięć znaków (wypełnianie zer po prawej stronie).
  • Powodem dodania dokładnie dziewięciu zer jest scenariusz gorszego przypadku, kiedy Math.random()funkcja zwróci dokładnie 0 lub 1 (prawdopodobieństwo 1/10 ^ 16 dla każdego z nich). Dlatego musieliśmy dodać do niego dziewięć zer ( "0"+"000000000"lub "1"+"000000000"), a następnie odciąć go od drugiego indeksu (3. znak) o długości ośmiu znaków. W pozostałych przypadkach dodanie zer nie zaszkodzi wynikowi, ponieważ i tak go odcina.
    Math.random().toString(16)+"000000000").substr(2,8).

Montaż:

  • Identyfikator GUID ma następujący format XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.
  • Podzieliłem GUID na 4 części, każda część podzielona na 2 typy (lub formaty): XXXXXXXXi -XXXX-XXXX.
  • Teraz buduję GUID za pomocą tych 2 rodzaje do zmontowania GUID z nazywają 4 sztuk, co następuje: XXXXXXXX -XXXX-XXXX -XXXX-XXXX XXXXXXXX.
  • Aby rozróżnić te dwa typy, dodałem parametr flagi do funkcji tworzenia par _p8(s), sparametr mówi funkcji, czy dodać myślniki, czy nie.
  • W końcu tworzymy GUID z następującym łańcuchem: _p8() + _p8(true) + _p8(true) + _p8()i zwracamy go.

Link do tego postu na moim blogu

Cieszyć się! :-)


13
Ta implementacja jest niepoprawna. Niektóre znaki GUID wymagają specjalnego traktowania (np. 13 cyfrą musi być cyfra 4).
JLRishe,

67

Oto kombinacja najczęściej głosowanej odpowiedzi z obejściem kolizji 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);
        });
    };

Na jsbin, jeśli chcesz go przetestować.


3
zauważ, że pierwsza wersja, jedna `window.crypto.getRandomValues , does not keep the Version 4 UUIDs format defined by RFC 4122. That is instead of xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx` daje xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
humanityANDpeace

66

Oto całkowicie niezgodna, ale bardzo wydajna implementacja w celu wygenerowania bezpiecznego dla ASCII unikalnego identyfikatora typu GUID.

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

Generuje 26 [a-z0-9] znaków, dając identyfikator UID, który jest zarówno krótszy, jak i bardziej unikalny niż identyfikatory GUID zgodne z RFC. Kreski można dodawać w trywialny sposób, jeśli liczy się czytelność dla człowieka.

Oto przykłady użycia i czasy dla tej funkcji oraz kilka innych odpowiedzi na to pytanie. Czas został wykonany w Chrome m25, każda z 10 milionami iteracji.

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

Oto kod czasowy.

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

62

Oto rozwiązanie z 9 października 2011 r. Z komentarza użytkownika jed na 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)}

Osiąga to ten sam cel, co obecnie najwyżej oceniana odpowiedź , ale w ponad 50 bajtach mniej poprzez wykorzystanie przymusu, rekurencji i notacji wykładniczej. Dla ciekawskich, jak to działa, oto adnotowana forma starszej wersji funkcji:

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

Z technicznego bloga 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;
}

Istnieją inne metody, które wymagają użycia kontrolki ActiveX, ale trzymaj się od nich z daleka!

Edycja: Pomyślałem, że warto zauważyć, że żaden generator GUID nie może zagwarantować unikatowych kluczy (zobacz artykuł na Wikipedii ). Zawsze istnieje szansa na kolizje. GUID po prostu oferuje wystarczająco duży wszechświat kluczy, aby zredukować zmianę kolizji do prawie zera.


8
Zauważ, że nie jest to GUID w sensie technicznym, ponieważ nie robi nic, aby zagwarantować wyjątkowość. To może, ale nie musi mieć znaczenia, w zależności od aplikacji.
Stephen Deken,

2
Szybka uwaga na temat wydajności. To rozwiązanie tworzy łącznie 36 ciągów, aby uzyskać pojedynczy wynik. Jeśli wydajność jest krytyczna, należy rozważyć utworzenie tablicę i łączenia, zgodnie z zaleceniami: tinyurl.com/y37xtx Dalsze badania wykazały, że nie może kwestia, więc YMMV: tinyurl.com/3l7945
Brandon Durette

2
Jeśli chodzi o wyjątkowość, warto zauważyć, że wersja 1.3 i 5 UUID są deterministyczne w sposób, w jaki wersja 4 nie jest. Jeśli dane wejściowe do tych generatorów UUID - identyfikator węzła w v1, przestrzeń nazw i nazwa w v3 i v5 - są unikalne (jak powinny być), to wynikowe UUID są unikalne. Teoretycznie zresztą.
broofa

41

Możesz użyć node-uuid ( https://github.com/kelektiv/node-uuid )

Prosta, szybka generacja UFCID RFC4122.

Funkcje:

  • Wygeneruj identyfikatory UUID RFC4122 w wersji 1 lub 4
  • Działa w node.js i przeglądarkach.
  • Kryptograficznie silne losowe generowanie na platformach pomocniczych.
  • Mały ślad (Chcesz coś mniejszego? Sprawdź to! )

Zainstaluj za pomocą NPM:

npm install uuid

Lub Korzystanie z UUID przez przeglądarkę:

Pobierz plik Raw (Uuid v1): https://raw.githubusercontent.com/kelektiv/node-uuid/master/v1.js Pobierz plik Raw (Uuid v4): https://raw.githubusercontent.com/kelektiv/node -uuid / master / v4.js


Chcesz jeszcze mniejszy? Sprawdź to: https://gist.github.com/jed/982883


Stosowanie:

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

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

EDYTOWAĆ:

Znowu odwiedziłem mój projekt, który korzystał z tej funkcji i nie podobało mi się gadatliwość. - Ale potrzebował odpowiedniej losowości.

Wersja oparta na odpowiedzi Briguy37 i kilku bitowych operatorach, aby wyodrębnić z bufora okna wielkości skubków.

Powinien być zgodny ze schematem RFC Type 4 (losowym), ponieważ miałem ostatnio problemy podczas analizowania niezgodnych UUID z UUID Javy.


31

Prosty moduł JavaScript jako kombinacja najlepszych odpowiedzi w tym wątku.

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

Stosowanie:

Guid.newGuid ()

„c6c2d12f-d76b-5739-e551-07e6de5b0807”

Guid.empty

„00000000-0000-0000-0000-000000000000”


1
Co przeszkadza o wszystkich odpowiedzi jest to, że wydaje się ok dla JavaScript, żeby przechowywać GUIDjako string. Twoja odpowiedź dotyczy przynajmniej znacznie bardziej wydajnego przechowywania za pomocą Uint16Array. toStringFunkcja powinna być pomocą reprezentację binarną w JavaScriptobject
Sebastian

Te identyfikatory UUID generowane przez ten kod są albo słabo zgodne, ale zgodne z RFC (_guid), lub silne, ale nie zgodne z RFC (_cryptoGuid). Ten pierwszy używa Math.random (), który jest obecnie znany jako zły RNG. Ten ostatni nie ustawia pól wersji i wariantów.
broofa

@broofa - Co sugerujesz, aby uczynić go silnym i zgodnym z RFC? I dlaczego _cryptoGuid nie jest zgodny z RFC?
Matt

@Matt _cryptoGuid () ustawia losowo wszystkie 128 bitów, co oznacza, że ​​nie ustawia pól wersji i wariantów zgodnie z opisem w RFC. Zobacz moją alternatywną implementację uuidv4 (), która używa crypto.getRandomValues ​​() w mojej najczęściej głosowanej odpowiedzi powyżej, dla silnej implementacji zgodnej z +.
Broofa

29

Tworzy to UUID w wersji 4 (utworzony z pseudolosowych liczb):

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

Oto próbka wygenerowanych UUID:

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

28

Cóż, ma to już wiele odpowiedzi, ale niestety nie ma w tym „prawdziwym” losowym przypadku. Poniższa wersja jest adaptacją odpowiedzi broofa, ale została zaktualizowana o „prawdziwą” losową funkcję, która wykorzystuje biblioteki kryptograficzne, o ile są dostępne, oraz funkcję Alea () jako awarię.

  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 <baagoe@baagoe.com>, 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 <baagoe@baagoe.com>, 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);
  });
};

27

Projekt JavaScript na GitHub - https://github.com/LiosK/UUID.js

UUID.js Generator UUID zgodny z RFC dla JavaScript.

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

Funkcje Generuje UUID zgodne z RFC 4122.

Dostępne są UUID w wersji 4 (UUID z liczb losowych) i UUID w wersji 1 (UUID oparte na czasie).

Obiekt UUID umożliwia różnorodny dostęp do UUID, w tym dostęp do pól UUID.

Niska rozdzielczość JavaScript w znacznikach czasu jest kompensowana przez liczby losowe.


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

16

Chciałem zrozumieć odpowiedź Broszy, więc ją rozwinąłem i dodałem komentarze:

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

Dziękujemy za szczegółowy opis! Szczególnie pomocne jest umieszczanie w klatkach między 8 a 11 z wyjaśnieniem ekwiwalentu.
Egor Litvinchuk

15

Dostosowałem mój własny generator UUID / GUID z kilkoma dodatkami tutaj .

Używam następującego generatora liczb losowych Kybos , aby być nieco bardziej kryptograficznie dobrym .

Poniżej znajduje się mój skrypt z wykluczonymi metodami Mash i Kybos z baagoe.com.

//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 <baagoe@baagoe.com>, 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));

15

Dla tych, którzy chcą rozwiązania zgodnego z rfc4122 w wersji 4 z uwzględnieniem prędkości (kilka wywoł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() );

Powyższa funkcja powinna mieć odpowiednią równowagę między prędkością a losowością.


13

Próbka ES6

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

12

Lepszy sposób:

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
 }

Zminimalizowane:

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}

11

Wiem, to stare pytanie. Dla kompletności, jeśli twoje środowisko to SharePoint, istnieje funkcja narzędzia o nazwie SP.Guid.newGuid( link msdn ), która tworzy nowy przewodnik. Ta funkcja znajduje się w pliku sp.init.js. Jeśli przepiszesz tę funkcję (aby usunąć niektóre inne zależności z innych funkcji prywatnych), wygląda to tak:

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

11

Ten jest oparty na dacie i dodaje losowy przyrostek, aby „zapewnić” wyjątkowość. Działa dobrze dla identyfikatorów css. Zawsze zwraca coś podobnego i jest łatwy do zhakowania:

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

11

Prosty kod, który wykorzystuje się crypto.getRandomValues(a)w obsługiwanych przeglądarkach (IE11 +, iOS7 +, FF21 +, Chrome, Android Chrome). Unika używania, Math.random()ponieważ może to powodować kolizje (na przykład 20 kolizji dla 4000 wygenerowanych płynów w rzeczywistej sytuacji przez 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);
}

Uwagi:

  • Zoptymalizowany pod kątem czytelności kodu, a nie prędkości, więc nadaje się na przykład kilkaset UUID na sekundę. Generuje około 10000 uuid () na sekundę w Chromium na moim laptopie za pomocą http://jsbin.com/fuwigo/1 do pomiaru wydajności.
  • Używa tylko 8 dla „y”, ponieważ upraszcza to czytelność kodu (y może wynosić 8, 9, A lub B).

11

Jeśli potrzebujesz tylko losowego ciągu 128-bitowego bez określonego formatu, możesz użyć:

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

Który zwróci coś takiego 2350143528-4164020887-938913176-2513998651.


BTW, dlaczego generuje tylko liczby, a nie także znaki? znacznie mniej bezpieczne
vsync

1
możesz także dodawać takie znaki (litery):Array.from((window.crypto || window.msCrypto).getRandomValues(new Uint32Array(4))).map(n => n.toString(16)).join('-')
magikMaker

11

Kolejny bardziej czytelny wariant z tylko dwiema mutacjami.

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

Cóż, większość deweloperów js jest programistami internetowymi i nie zrozumiemy, co robią operatorzy bitowi, ponieważ nie używamy ich przez większość czasu, gdy rozwijamy. Właściwie nigdy nie potrzebowałem żadnego z nich i jestem programistą od 1997 roku. Twój przykładowy kod jest nadal całkowicie nieczytelny dla przeciętnego programisty, który go przeczyta. Nie wspominając już o tym, że nadal używasz nazw zmiennych zawierających jedną literę, co czyni ją jeszcze bardziej tajemniczą. Prawdopodobnie czytam Clean Code, może to pomaga: amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/…
inf3rno

@ inf3rno nie bój go, wszystkie proponowane rozwiązania w tym wątku są tajemnicze, ale są poprawnymi odpowiedziami, biorąc pod uwagę, że pytaniem było mieć jedno linijkę. właśnie one-linery są tajemnicze. nie mogą sobie pozwolić na czytelność dla przeciętnego dewelopera, ale oszczędzają nieruchomości ekranowe, na których wystarczy prosty komentarz poprzedzający. W rezultacie staje się znacznie bardziej czytelny w ten sposób, niż gdyby zamiast tego był w „czytelnym kodzie”.
tatsu

Losowo! = Unikatowy
1529413

@ user1529413 Tak. Unikalność wymaga indeksu.
Ceving

To moja ulubiona odpowiedź, ponieważ buduje UUID jako 16-bajtową (128-bitową) wartość, a nie jest serializowaną, przyjemną do czytania formą. Byłoby banalnie łatwo upuścić łańcuch znaków i po prostu ustawić poprawne bity losowego 128-bitowego, co jest wszystkim, czym musi być uuidv4. Możesz base64 go dla krótszych adresów URL, przekazać go z powrotem do jakiegoś zestawu stron internetowych, przechowywać go w mniejszej ilości pamięci niż jako ciąg, uczynić go buforem wielkości 4096 i umieścić w nim 256 uuids, przechowywać w db przeglądarki itp. Znacznie lepiej niż wszystko od początku jako długi, pisany małymi literami ciąg w kodzie szesnastkowym.
Josh z Qaribou

8

OK, używając uuid pakiet, to wsparcie dla wersji 1, 3, 4 i 5 UUID zrobić:

yarn add uuid

i wtedy:

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

Możesz to również zrobić przy użyciu w pełni określonych opcji:

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'

Aby uzyskać więcej informacji, odwiedź stronę npm tutaj


6

Ważne jest, aby używać dobrze przetestowanego kodu, który jest obsługiwany przez więcej niż 1 współautorów, zamiast biczować własne rzeczy. Jest to jedno z miejsc, w których prawdopodobnie wolisz najbardziej stabilny kod niż najkrótszą możliwą sprytną wersję, która działa w przeglądarce X, ale nie bierze pod uwagę osobliwości Y, co często prowadzi do bardzo trudnych do zbadania błędów niż manifestuje się tylko losowo dla niektórych użytkowników. Osobiście używam uuid-js na https://github.com/aurigadl/uuid-js, w którym włączono altanę, dzięki czemu mogę łatwo pobierać aktualizacje.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.