Pytanie Utwórz GUID / UUID w JavaScript?


Próbuję tworzyć globalnie unikatowe identyfikatory w JavaScript. Nie jestem pewien, jakie procedury są dostępne we wszystkich przeglądarkach, jak "losowy" i wysiewany jest wbudowany generator liczb losowych, itd ..

Identyfikator GUID / UUID powinien mieć co najmniej 32 znaki i powinien pozostać w zakresie ASCII, aby uniknąć problemów podczas ich przekazywania.


3219


pochodzenie


Identyfikatory GUID, gdy są odrzucane jako łańcuchy, mają co najmniej 36 znaków i nie więcej niż 38 znaków i pasują do wzorca ^ \ {? [A-zA-Z0-9] {36}? \} $ I dlatego zawsze są ascii. - AnthonyWJones
David Bau zapewnia znacznie lepszy, nadający się do siewu generator liczb losowych na davidbau.com/archives/2010/01/30/... Napisałem nieco inne podejście do generowania UUID w blogs.cozi.com/tech/2010/04/generating-uuids-in-javascript.html - George V. Reilly
jsben.ch/#/Lbxoe - tutaj benchmark z różnymi funkcjami od dołu - EscapeNetscape
uuid-random używa dobrego PRNG i jest bardzo szybki. - jchook
Późno (bardzo późno!) Na imprezę tutaj, ale @AnthonyWJones nie powinno być twoje wyrazy regularne: ^ \ {? [A-fA-F0-9] {36}? \} $ - noonand


Odpowiedzi:


Było kilka prób na to. Pytanie brzmi: czy chcesz rzeczywiste identyfikatory GUID, czy tylko przypadkowe liczby, które Popatrz jak identyfikatory GUID? Łatwo jest generować losowe liczby.

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

Należy jednak pamiętać, że takie wartości nie są oryginalnymi identyfikatorami GUID.

Nie ma sposobu na wygenerowanie prawdziwych identyfikatorów GUID w JavaScript, ponieważ zależą one od właściwości komputera lokalnego, którego przeglądarki nie eksponują. Będziesz musiał użyć usług specyficznych dla systemu operacyjnego, takich jak ActiveX: http://p2p.wrox.com/topicindex/20339.htm

Edycja: niepoprawna - RFC4122 umożliwia losowe ("wersja 4") identyfikatory GUID. Zobacz inne odpowiedzi, aby poznać szczegóły.

Uwaga: podany fragment kodu nie jest zgodny z RFC4122, który wymaga, aby wersja (4) musi być zintegrowany z wygenerowanym łańcuchem wyjściowym. Nie używaj tej odpowiedzi jeśli potrzebujesz zgodnych identyfikatorów GUID.

Posługiwać się:

var uuid = guid();

Próbny:

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


1904



W rzeczywistości RFC zezwala na identyfikatory UUID utworzone z liczb losowych. Trzeba tylko pokroić kilka bitów, aby to zidentyfikować. Zobacz punkt 4.4. Algorytmy tworzenia identyfikatora UUID z liczb losowych lub pseudolosowych: rfc-archive.org/getrfc.php?rfc=4122 - Jason DeFontes
Czy ktoś mógłby mi wytłumaczyć ten kod? Wygląda na to, że funkcja S4 próbuje uzyskać losowy numer heksadecymalny między 0x10000 i 0x20000, a następnie wypisuje ostatnie 4 cyfry. Ale dlaczego bitowe lub z 0? Czy to nie powinien być odjazd? Czy od 0x10000 do 0x20000 jest tylko hack, aby uniknąć konieczności radzenia sobie z wiodącymi zerami? - Cory
W Chrome ten kod nie zawsze generuje GUID o prawidłowym rozmiarze. Długość waha się od 35 do 36 - cdeutsch
W jaki sposób tak ewidentnie błędna odpowiedź może doprowadzić do tak wielu przebojów? Nawet kod jest błędny, ponieważ nie ma 4 na właściwej pozycji. en.wikipedia.org/wiki/Globally_unique_identifier - Dennis Krøger
Ta odpowiedź została złamana w wersji 5, gdy usunięto "1 + ..." i "podciąg (1)". Te bity gwarantowały stałą długość. - Segfault


Na RFC4122 rozwiązanie zgodne z 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 zależy w dużej mierze od generatora liczb losowych (RNG). Powyższe rozwiązanie wykorzystuje Math.random() dla zwięzłości jednak Math.random() jest nie gwarantowane jako wysokiej jakości RNG. Zobacz Adama Hylanda doskonałe napisanie na Math.random () dla szczegółów. Aby uzyskać bardziej niezawodne rozwiązanie, rozważ coś w stylu moduł uuid[Zastrzeżenie: jestem autorem], który używa lepszych API RNG tam, gdzie są dostępne.

Aktualizacja, 2015-08-26: Jako uwaga, to sens opisuje, jak określić, ile identyfikatorów można wygenerować przed osiągnięciem określonego prawdopodobieństwa kolizji. Na przykład z 3.26x1015 Wersja 4 UUID RFC4122 masz 1 na milion szans na kolizję.

Aktualizacja, 2017-06-28: A dobry artykuł od programistów Chrome omawianie stanu jakości Math.random PRNG w Chrome, Firefox i Safari. tl; dr - Od końca 2015 roku jest "całkiem niezły", ale nie ma jakości kryptograficznej. Aby rozwiązać ten problem, oto zaktualizowana wersja powyższego rozwiązania, która używa ES6, crypto API, i odrobinę magii JS, której nie mogę wziąć pod uwagę:

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


3150



Czy można bezpiecznie używać tego kodu do generowania niepowtarzalnych identyfikatorów na kliencie, a następnie używać tych identyfikatorów jako kluczy podstawowych do zapisywania obiektów na serwerze? - Muxa
... (ciąg dalszy) Szanse na dwa identyfikatory generowane przez kolizję tej funkcji są, dosłownie, astronomicznie małe. Wszystkie z 6 ze 128 bitów identyfikatora są generowane losowo, co oznacza, że ​​dla dowolnych dwóch identyfikatorów istnieje 1 do 2 ^^ 122 (lub 5,3x10 ^^ 36) szansa, że ​​zderzają się. - broofa
Wysłałem pytanie o kolizje stackoverflow.com/questions/6906916/... - Muxa
Na pewno odpowiedzią na pytanie @ Muxy jest "nie"? Zaufanie do czegoś, co pochodzi od klienta, nigdy nie jest bezpieczne. Wydaje mi się, że zależy to od tego, jak bardzo użytkownicy będą wyświetlać konsolę javascript i ręcznie zmieniać zmienną, aby uzyskać coś, czego potrzebują. Lub mogą po prostu POSTĄĆ z powrotem id, że chcą. Zależy również od tego, czy użytkownik wybierający własny identyfikator spowoduje luki w zabezpieczeniach. Tak czy inaczej, jeśli jest to losowy numer identyfikacyjny, który wchodzi do tabeli, prawdopodobnie generowałbym go po stronie serwera, więc wiem, że mam kontrolę nad procesem. - Cam Jackson
@DrewNoakes - identyfikatory UUID to nie tylko ciąg całkowicie losowych # -ów. "4" to wersja uuid (4 = "losowa"). Znaki "y", w których musi być osadzony wariant uuid (w zasadzie układ pola). Zobacz sekcje 4.1.1 i 4.1.3 ietf.org/rfc/rfc4122.txt po więcej informacji. - broofa


Bardzo podoba mi się, jak czysty Odpowiedź Broofy jest, ale to niefortunne, że słabe implementacje Math.random zostaw szansę na kolizję.

Oto podobne RFC4122 Rozwiązanie zgodne z wersją 4, które rozwiązuje ten problem, kompensując pierwsze 13 liczb szesnastkowych za pomocą szesnastkowej części znacznika czasu. W ten sposób, nawet jeśli Math.randomznajduje się na tym samym źródle, obaj klienci będą musieli wygenerować UUID dokładnie w tej samej milisekundy (lub 10 000 lat później), aby uzyskać ten sam UUID:

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


Oto skrzypce do przetestowania.


672



Miej na uwadze, new Date().getTime() nie jest aktualizowany co milisekundę. Nie jestem pewien, w jaki sposób wpływa to na oczekiwaną losowość twojego algorytmu. - devios1
Myślę, że jest to najlepsza odpowiedź tylko dlatego, że używa daty w jej generacji. Jednak jeśli masz nowoczesny stos przeglądarek, polecam Date.now() do new Date().getTime() - Fresheyeball
performance.now byłoby jeszcze lepiej. W przeciwieństwie do Date.now, znaczniki czasu zwracane przez performance.now() nie są ograniczone do rozdzielczości jednej milisekundy. Zamiast tego reprezentują one czasy jako liczby zmiennoprzecinkowe o wartości do dokładność mikrosekundy. Również w przeciwieństwie do Date.now, wartości zwracane przez performance.now () zawsze rosną ze stałą stawką, niezależnie od zegara systemowego, który może być korygowany ręcznie lub przekrzywiony przez oprogramowanie takie jak Network Time Protocol. - daniellmb
@ Daniellmb Prawdopodobnie powinieneś połączyć się z MDN lub innym, aby pokazać prawdziwą dokumentację, a nie polyfill;) - Martin
Do tego celu na stopce witryny, wszystkie udziały użytkowników na stronie są dostępne na licencji cc by-sa 3.0. - Xiong Chiamiov


Odpowiedź Broofy jest całkiem sprytna - naprawdę imponująca, naprawdę ... zgodna z rfc4122, w pewnym sensie czytelna i zwarta. Niesamowite!

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

Ale, możemy uzyskać prędkość i Zgodność z RFC? Powiedziałem tak!  Czy możemy utrzymać czytelność? Cóż ... Nie bardzo, ale to łatwe, jeśli podążysz za nim.

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

Tak więc, po mojej szóstej iteracji optymalizacji, pokonałem najbardziej popularną odpowiedź 12X, zaakceptowana odpowiedź od nowa 9Xi szybka, nie spełniająca wymagań odpowiedź 2-3X. I nadal jestem zgodny z rfc4122.

Interesuje Cię jak? Włożyłem pełne źródło http://jsfiddle.net/jcward/7hyaC/3/ i dalej http://jsperf.com/uuid-generator-opt/4

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

Więc zastępuje x z dowolną losową cyfrą szesnastkową, y z losowymi danymi (z wyjątkiem wymuszania 2 najwyższych bitów na 10 według specyfikacji RFC), a wyrażenie regularne nie pasuje do - lub 4 bohaterów, więc nie musi się nimi zajmować. Bardzo, bardzo śliskie.

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

Pierwszym krokiem w kierunku wydajności jest wyeliminowanie RegEx i jego funkcji wywołania zwrotnego i użycie prostej pętli. Oznacza to, że mamy do czynienia z - i 4 znaków, podczas gdy broofa nie. Zwróć też uwagę, że możemy używać indeksowania String Array, aby zachować jego płynną architekturę szablonów String:

function e1() {
  var u='',i=0;
  while(i++<36) {
    var c='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'[i-1],r=Math.random()*16|0,v=c=='x'?r:(r&0x3|0x8);
    u+=(c=='-'||c=='4')?c:v.toString(16)
  }
  return u;
}

Zasadniczo ta sama wewnętrzna logika, z wyjątkiem tego, którą sprawdzamy - lub 4i użycie pętli while (zamiast replace() callbacks) poprawia nam prawie 3X poprawę!

Następnym krokiem jest mały na pulpicie, ale przyzwoicie różni się on na urządzeniach mobilnych. Zróbmy mniej wywołań Math.random () i wykorzystajmy wszystkie te losowe bity, zamiast wyrzucać 87% z nich losowym buforem, który zostanie przesunięty w każdej iteracji. Usuńmy także tę definicję szablonu z pętli, na wypadek gdyby pomogło:

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
}

To pozwala nam zaoszczędzić 10-30% w zależności od platformy. Nie jest zły. Ale kolejnym dużym krokiem jest pozbycie się funkcji toString w połączeniu z klasą optymalizacji - tabelą look-up. 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
}

Kolejna optymalizacja to kolejny klasyk. Ponieważ obsługujemy tylko 4-bitowe dane wyjściowe w każdej iteracji pętli, zmniejszmy liczbę pętli na pół i przetwarzaj 8 bitów w każdej iteracji. Jest to trudne, ponieważ nadal musimy obsługiwać pozycje bitowe zgodne z RFC, ale nie jest to zbyt trudne. Następnie musimy utworzyć większą tabelę odnośników (16x16 lub 256) do przechowywania 0x00 - 0xff, a my 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
}

Próbowałem e6 (), który przetwarza 16-bitów naraz, wciąż używając 256-elementowego LUT i pokazał malejące wyniki optymalizacji. Pomimo mniejszej ilości iteracji wewnętrzna logika była skomplikowana przez zwiększone przetwarzanie i była taka sama na komputerach i tylko o 10% szybciej na urządzeniach mobilnych.

Ostateczna technika optymalizacji do zastosowania - rozwinąć pętlę. Ponieważ robimy pętlę określoną liczbę razy, możemy technicznie napisać to wszystko ręcznie. Próbowałem tego raz z jedną zmienną losową r, którą ciągle zmieniałem, a wydajność tankuje. Ale z czterema zmiennymi przypisanymi losowymi danymi z góry, a następnie przy użyciu tabeli odnośników i stosowaniem właściwych 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];
}

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

Zabawne jest to, że generowanie 16 bajtów danych losowych jest łatwą częścią. Cała sztuczka polega na wyrażaniu go w formacie String z zachowaniem zgodności z RFC, i jest to najbardziej szczelne z 16 bajtami losowych danych, rozwiniętą pętlą i tabelą odnośników.

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

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


308



jsperf.com pozwoli Ci przechwycić dane i zobaczyć wyniki w różnych przeglądarkach i urządzeniach. - fearphage
Cześć @chad, dobre pytania. k może zostać przeniesiony na zewnątrz, ale to nie poprawi wydajności powyżej i spowoduje większy bałagan. A budowanie tablicy i dołączanie po powrocie dziwnie zabija wydajność. Ale znowu, nie krępuj się eksperymentować! - Jeff Ward
Byłbym ciekawy, jak porównać węzeł uuid.js. W mojej pracy zacząłem od większej koncentracji na osiągach, z których część pozostaje. Ale od tego czasu wycofałem się z tego, preferując bardziej czytelny / możliwy do utrzymania kod. Powodem jest to, że uuid perf po prostu nie jest problemem w prawdziwym świecie. Uuidy są zwykle tworzone w połączeniu z dużo wolniejszymi operacjami (na przykład tworzeniem żądania sieciowego, tworzeniem i utrwalaniem obiektu modelu), gdzie golenie kilka mikrosekund od rzeczy po prostu nie ma znaczenia. - broofa
Ten kod nadal zawiera kilka błędów: Math.random()*0xFFFFFFFF linie powinny być Math.random()*0x100000000 dla pełnej losowości, i >>>0 należy użyć zamiast |0 aby zachować niepodpisane wartości (choć przy obecnym kodzie, wydaje mi się, że wygasa, mimo że są podpisane). W końcu byłby to bardzo dobry pomysł w dzisiejszych czasach window.crypto.getRandomValues jeśli są dostępne, i powracają do Math.random tylko wtedy, gdy jest to absolutnie konieczne. Math.random może mieć mniej niż 128 bitów entropii, w którym to przypadku byłaby bardziej podatna na zderzenia niż to konieczne. - Dave
Zastosowałem wszystkie rady @Dave i opublikowałem bardzo schludne źródło ES6 / Babel tutaj: codepen.io/avesus/pen/wgQmaV?editors=0012 - Brian Haak


Oto kod na podstawie RFC 4122, rozdział 4.4 (Algorytmy tworzenia identyfikatora UUID z naprawdę losowego lub pseudolosowego).

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

138



To nie tworzy kresek potrzebnych dla c # do parsowania go w System.Guid. Powtarza się tak: B42A153F1D9A4F92990392C11DD684D2, kiedy powinien renderować jak: B42A153F-1D9A-4F92-9903-92C11DD684D2 - Levitikon
ABNF ze specyfikacji zawiera znaki "-", więc zaktualizowałem je tak, aby były zgodne. - Kevin Hakanson
Osobiście nienawidzę kresek, ale do każdego z nich. Cześć, dlatego jesteśmy programistami! - devios1
Powinieneś zadeklarować rozmiar tablicy wcześniej, zamiast dynamicznie zmieniać jej rozmiar podczas budowania GUID. var s = new Array(36); - MgSam
@Levitikon .NET's Guid.Parse() powinien parsować B42A153F1D9A4F92990392C11DD684D2 do Guida w porządku. Nie musi mieć łączników. - JLRishe


Najszybsza metoda GUID generatora ciągów w formacie XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX. Nie generuje to GUID zgodnego ze standardami.

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

Ta funkcja jest tak prosta, jak:

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

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 być może jest co najmniej jedna osoba, która potrzebuje wyjaśnienia:

Algorytm:

  • The Math.random() Funkcja zwraca liczbę dziesiętną z przedziału od 0 do 1 z 16 cyframi po przecinku dziesiętnym (dla przykład 0.4363923368509859).
  • Następnie bierzemy tę liczbę i przeliczamy do sznurka z podstawą 16 (z powyższego przykładu otrzymamy) 0.6fb7687f).
    Math.random().toString(16).
  • Potem odcięliśmy 0. prefiks (0.6fb7687f => 6fb7687f) i uzyskaj ciąg znaków zawierający osiem znaków szesnastkowych postacie długie.
    (Math.random().toString(16).substr(2,8).
  • Czasami Math.random()funkcja powróci krótszy numer (na przykład 0.4363), z powodu zer na końcu (z powyższego przykładu, faktycznie liczba jest 0.4363000000000000). Właśnie dlatego dołączam do tego łańcucha "000000000" (ciąg z dziewięcioma zerami), a następnie odcięcie go substr() funkcja, która ma dokładnie dziewięć znaków (wypełnianie zer po prawej).
  • Powodem dodania dokładnie dziewięciu zera jest gorszy scenariusz, czyli 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 odcinając go od drugiego indeksu (3 znak) o długości ośmiu znaków. W pozostałych przypadkach dodanie zera 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.
  • Podzielam GUID na 4 części, z których każda jest podzielona na 2 rodzaje (lub formaty): XXXXXXXX i -XXXX-XXXX.
  • Teraz buduję GUID za pomocą tych 2 typów, aby połączyć GUID z 4 częściami wywołania, jak następuje: XXXXXXXX  -XXXX-XXXX  -XXXX-XXXX  XXXXXXXX.
  • Aby odróżnić te dwa typy, dodałem parametr flagi do funkcji tworzenia par _p8(s), s parametr mówi, czy dodać kreski, czy nie.
  • W końcu tworzymy GUID z następującym łańcuchem: _p8() + _p8(true) + _p8(true) + _p8()i zwróć to.

Link do tego posta na moim blogu

Cieszyć się! :-)


79



Ta implementacja jest niepoprawna. Niektóre znaki GUID wymagają specjalnego traktowania (np. 13 cyfr musi być numerem 4). - JLRishe
@JLRishe, masz rację, nie przestrzega standardów RFC4122. Ale to wciąż losowy ciąg, który wygląda jak GUID. Twoje zdrowie :-) - Slavik Meltser
Dobra robota, ale klasyczne techniki optymalizacyjne sprawiają, że jest 6 razy szybsza (w mojej przeglądarce) - patrz moja odpowiedź - Jeff Ward


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

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

Jeśli dwa identyfikatory są generowane w krótszych odstępach czasu, i zakładając, że losowa metoda jest prawdziwie losowa, wygenerowałoby to ID, które są 99,99999999999999% prawdopodobnie globalnie unikalne (kolizja w 1 z 10 ^ 15)

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

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


69



To nie jest jednak UUID? - Marco Kerwitz
Nie. Identyfikator UUID / GUID to 122-bitowy (+ sześć zarezerwowanych bitów) numer. może zagwarantować wyjątkowość za pośrednictwem globalnej usługi licznika, ale często przekaże na czas, adres MAC i losowość. UUID nie są losowe! UID, który tutaj sugeruję, nie jest w pełni skompresowany. Możesz skompresować go do 122-bitowej liczby całkowitej, dodać 6 predefiniowanych bitów i dodatkowe losowe bity (usunąć kilka bitów z zegarem), a skończysz z doskonale ukształtowanym identyfikatorem UUID / GUID, który następnie musisz przekonwertować na hex. Dla mnie to naprawdę nie dodaje niczego oprócz zgodności do długości identyfikatora. - Simon Rigét
Przekazywanie adresów MAC dla unikalności na maszynach wirtualnych to zły pomysł! - Simon Rigét


Oto kombinacja najlepsza głosowana odpowiedź, z obejściem dla Kolizje Chrome:

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

Na jsbin jeśli chcesz to przetestować.


57



Wierzę, że w IE jest to w rzeczywistości window.msCrypto zamiast window.crypto. Może być fajnie sprawdzić dla obu. Widzieć msdn.microsoft.com/en-us/library/ie/dn265046(v=vs.85).aspx - herbrandson
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` to ustępuje xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. - humanityANDpeace


Oto rozwiązanie z 9 października 2011 r. Z komentarza użytkownika jed w 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 ten sam cel, co obecna najwyższa ocena odpowiedzi, ale w 50+ mniejszej liczbie bajtów, wykorzystując wymuszenie, rekurencję i notację wykładniczą. Dla ciekawych jak to działa, oto opisana 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



W TypeScript użyj tego: export const UUID = function b (a: number): string { return a ? (a^Math.random()*16>>a/4).toString(16) : (''+[1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, b) }; - RyanNerd