Pertanyaan Buat GUID / UUID di JavaScript?


Saya mencoba membuat pengidentifikasi unik global dalam JavaScript. Saya tidak yakin rutinitas apa yang tersedia di semua browser, bagaimana "acak" dan diunggulkan pembuat nomor acak built-in, dll.

GUID / UUID harus setidaknya 32 karakter dan harus tetap dalam kisaran ASCII untuk menghindari masalah ketika meneruskannya.


3207


asal


Jawaban:


Ada beberapa upaya dalam hal ini. Pertanyaannya adalah: apakah Anda ingin GUIDs yang sebenarnya, atau hanya angka acak itu melihat seperti GUID? Ini cukup mudah untuk menghasilkan angka acak.

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

Namun, perhatikan bahwa nilai-nilai tersebut bukan GUID asli.

Tidak ada cara untuk menghasilkan GUID yang sebenarnya dalam Javascript, karena mereka bergantung pada properti komputer lokal yang tidak diekspos oleh browser. Anda harus menggunakan layanan khusus OS seperti ActiveX: http://p2p.wrox.com/topicindex/20339.htm

Edit: tidak benar - RFC4122 memungkinkan GUID acak ("versi 4"). Lihat jawaban lain untuk spesifikasinya.

Catatan: snipet kode yang disediakan tidak mengikuti RFC4122 yang mengharuskan versi tersebut (4) harus diintegrasikan ke dalam string output yang dihasilkan. Jangan gunakan jawaban ini jika Anda membutuhkan GUID yang sesuai.

Menggunakan:

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



Untuk sebuah RFC4122 solusi compliant versi 4, solusi satu-liner (ish) ini adalah yang paling ringkas yang bisa saya buat:

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

Perbarui, 2015-06-02: Sadarilah bahwa UUID keunikan sangat bergantung pada pembuat nomor acak (RNG) yang mendasari. Solusi di atas menggunakan Math.random() untuk keringkasan, bagaimanapun Math.random() aku s tidak dijamin menjadi RNG berkualitas tinggi. Lihat Adam Hyland Langgan yang bagus di Math.random () untuk detailnya. Untuk solusi yang lebih kuat, pertimbangkan sesuatu seperti modul uuid[Penafian: Saya adalah penulis], yang menggunakan API RNG berkualitas tinggi jika tersedia.

Perbarui, 2015-08-26: Sebagai catatan sampingan, ini inti menjelaskan cara menentukan berapa banyak ID yang dapat dihasilkan sebelum mencapai probabilitas tabrakan tertentu. Misalnya, dengan 3,26x1015 versi 4 RFC4122 UUID Anda memiliki peluang 1-in-a-juta tabrakan.

Perbarui, 2017-06-28: SEBUAH artikel bagus dari pengembang Chrome membahas keadaan kualitas PRNG Matematika di Chrome, Firefox, dan Safari. tl; dr - Pada akhir-2015 itu "cukup bagus", tapi bukan kualitas kriptografi. Untuk mengatasi masalah itu, inilah versi terbaru dari solusi di atas yang menggunakan ES6, crypto API, dan sedikit JS wizardy saya tidak bisa mengambil kredit untuk:

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



Saya sangat suka bagaimana bersih Jawaban Broofa adalah, tetapi disayangkan bahwa implementasi yang buruk Math.random biarkan peluang tabrakan.

Ini sama RFC4122 versi 4 solusi yang memenuhi solusi yang memecahkan masalah itu dengan mengimbangi 13 angka hex pertama dengan bagian heksa stempel waktu. Dengan begitu, biarpun Math.randomadalah pada benih yang sama, kedua klien harus menghasilkan UUID pada milidetik yang sama (atau 10.000+ tahun kemudian) untuk mendapatkan UUID yang sama:

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


Berikut ini biola untuk diuji.


671



Jawaban broofa cukup licin, memang - benar-benar pintar, benar-benar ... rfc4122 compliant, mudah dibaca, dan ringkas. Luar biasa!

Tetapi jika Anda melihat ekspresi reguler itu, itu banyak replace() panggilan balik, toString()dan Math.random() fungsi panggilan (di mana dia hanya menggunakan 4 bit hasil dan membuang sisanya), Anda mungkin mulai bertanya-tanya tentang kinerja. Memang, joelpt bahkan memutuskan untuk membuang RFC untuk kecepatan GUID umum dengan generateQuickGUID.

Tapi, bisakah kita mendapatkan kecepatan dan Kepatuhan RFC? Saya katakan, YA!  Bisakah kita menjaga keterbacaan? Yah ... Tidak juga, tapi itu mudah jika kamu mengikutinya.

Tapi pertama, hasil saya, dibandingkan dengan broofa, guid (jawaban yang diterima), dan non-rfc-compliant 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.

Jadi dengan pengoptimalan optimisasi ke-6 saya, saya mengalahkan jawaban yang paling populer dengan lebih dari itu 12X, jawaban yang diterima oleh lebih 9X, dan jawaban cepat yang tidak sesuai dengan 2-3X. Dan saya masih rfc4122 compliant.

Tertarik bagaimana caranya? Saya sudah memasukkan sumber lengkap http://jsfiddle.net/jcward/7hyaC/3/ dan terus http://jsperf.com/uuid-generator-opt/4

Untuk penjelasan, mari kita mulai dengan kode 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);
});

Jadi itu menggantikan x dengan sembarang digit hex acak, y dengan data acak (kecuali memaksa 2 bit teratas ke 10 per spesifikasi RFC), dan regex tidak cocok dengan - atau 4 karakter, jadi dia tidak harus berurusan dengan mereka. Sangat, sangat licin.

Hal pertama yang harus diketahui adalah bahwa fungsi panggilan mahal, seperti juga ekspresi reguler (meskipun ia hanya menggunakan 1, ia memiliki 32 callback, satu untuk setiap pertandingan, dan di masing-masing dari 32 callback yang dipanggil Math.random () dan v. toString (16)).

Langkah pertama menuju performa adalah menghilangkan RegEx dan fungsi callback dan menggunakan loop sederhana sebagai gantinya. Ini berarti kita harus berurusan dengan - dan 4 karakter sedangkan broofa tidak. Juga, perhatikan bahwa kita dapat menggunakan pengindeksan String Array untuk menjaga arsitektur template String nya yang licin:

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

Pada dasarnya, logika batin yang sama, kecuali kita periksa - atau 4, dan menggunakan loop sementara (bukan replace() callback) memberi kita peningkatan hampir 3X!

Langkah selanjutnya adalah langkah kecil di desktop tetapi membuat perbedaan yang lumayan di seluler. Mari kita membuat lebih sedikit Math.random () memanggil dan memanfaatkan semua bit acak daripada membuang 87% dari mereka pergi dengan buffer acak yang digeser keluar setiap iterasi. Mari kita pindahkan definisi template itu dari loop, kalau-kalau itu membantu:

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
}

Ini menghemat 10-30% tergantung pada platform. Tidak buruk. Namun langkah besar berikutnya menyingkirkan fungsi panggilan toString sama sekali dengan pengoptimalan klasik - tabel pencarian. Tabel lookup 16-elemen sederhana akan melakukan pekerjaan toString (16) dalam waktu yang jauh lebih sedikit:

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
}

Pengoptimalan berikutnya adalah klasik lain. Karena kita hanya menangani 4-bit output dalam setiap iterasi loop, mari kita memotong jumlah loop menjadi dua dan memproses 8-bit setiap iterasi. Ini rumit karena kami masih harus menangani posisi bit RFC yang sesuai, tetapi itu tidak terlalu sulit. Kami kemudian harus membuat tabel pencarian yang lebih besar (16x16, atau 256) untuk menyimpan 0x00 - 0xff, dan kami membuatnya hanya sekali, di luar fungsi 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
}

Saya mencoba sebuah e6 () yang memproses 16-bit pada satu waktu, masih menggunakan LUT 256-elemen, dan itu menunjukkan hasil optimasi yang semakin berkurang. Meskipun memiliki iterasi yang lebih sedikit, logika batiniah dipersulit oleh peningkatan pemrosesan, dan itu melakukan hal yang sama di desktop, dan hanya ~ 10% lebih cepat di seluler.

Teknik optimasi akhir untuk menerapkan - membuka gulungan loop. Karena kami mengulang beberapa kali, kami dapat secara teknis menulis ini semua dengan tangan. Saya mencoba ini sekali dengan r variabel acak tunggal yang saya terus menetapkan kembali, dan kinerja tanked. Tetapi dengan empat variabel yang ditugaskan data acak di depan, kemudian menggunakan tabel pencarian, dan menerapkan bit RFC yang tepat, versi ini menghisap mereka semua:

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

Lucunya, menghasilkan 16 byte data acak adalah bagian yang mudah. Seluruh trik ini mengekspresikannya dalam format String dengan kepatuhan RFC, dan itu paling erat dicapai dengan 16 byte data acak, loop dan tabel pencarian yang tidak terkontrol.

Saya harap logika saya benar - sangat mudah untuk membuat kesalahan dalam jenis pekerjaan yang membosankan ini. Tapi hasilnya terlihat bagus untukku. Saya harap Anda menikmati perjalanan gila ini melalui pengoptimalan kode!

Disarankan: tujuan utama saya adalah untuk menunjukkan dan mengajarkan strategi pengoptimalan potensial. Jawaban lain mencakup topik-topik penting seperti tabrakan dan nomor yang benar-benar acak, yang penting untuk menghasilkan UUID yang baik.


305



Berikut beberapa kode berdasarkan RFC 4122, bagian 4.4 (Algoritma untuk Membuat UUID dari Nomor Acak atau Pseudo-Acak Sesungguhnya).

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



GUID tercepat seperti metode string generator dalam format XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX. Ini tidak menghasilkan GUID standar-compliant.

Sepuluh juta eksekusi dari implementasi ini hanya membutuhkan waktu 32,5 detik, yang merupakan tercepat yang pernah saya lihat di browser (satu-satunya solusi tanpa loop / iterasi).

Fungsi ini sesederhana:

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

Untuk menguji performanya, Anda dapat menjalankan kode ini:

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

Saya yakin sebagian besar dari Anda akan mengerti apa yang saya lakukan di sana, tetapi mungkin ada setidaknya satu orang yang membutuhkan penjelasan:

Algoritme:

  • Itu Math.random() fungsi mengembalikan angka desimal antara 0 dan 1 dengan 16 digit setelah titik pecahan desimal (untuk contoh 0.4363923368509859).
  • Lalu kami mengambil nomor ini dan mengonversi ke string dengan basis 16 (dari contoh di atas kita akan dapatkan 0.6fb7687f).
    Math.random().toString(16).
  • Lalu kita memotong 0. awalan (0.6fb7687f => 6fb7687f) dan mendapatkan string dengan delapan heksadesimal karakter panjang.
    (Math.random().toString(16).substr(2,8).
  • Terkadang Math.random()fungsi akan kembali jumlah yang lebih pendek (misalnya 0.4363), karena nol di akhir (dari contoh di atas, sebenarnya jumlahnya 0.4363000000000000). Itu sebabnya saya menambahkan string ini "000000000" (string dengan sembilan nol) dan kemudian memotongnya dengan substr() berfungsi untuk membuatnya sembilan karakter persis (mengisi nol ke kanan).
  • Alasan menambahkan tepat sembilan nol adalah karena skenario kasus yang lebih buruk, yaitu ketika Math.random() fungsi akan mengembalikan tepat 0 atau 1 (kemungkinan 1/10 ^ 16 untuk masing-masing dari mereka). Itu sebabnya kami perlu menambahkan sembilan nol untuk itu ("0"+"000000000" atau "1"+"000000000"), dan kemudian memotongnya dari indeks kedua (karakter ke-3) dengan panjang delapan karakter. Untuk sisa kasus, penambahan nol tidak akan merusak hasil karena memotongnya juga.
    Math.random().toString(16)+"000000000").substr(2,8).

Perakitan:

  • GUID berada dalam format berikut XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.
  • Saya membagi GUID menjadi 4 bagian, masing-masing bagian dibagi menjadi 2 jenis (atau format): XXXXXXXX dan -XXXX-XXXX.
  • Sekarang saya sedang membangun GUID menggunakan 2 jenis ini untuk merakit GUID dengan memanggil 4 buah, sebagai berikut: XXXXXXXX  -XXXX-XXXX  -XXXX-XXXX  XXXXXXXX.
  • Untuk membedakan kedua jenis ini, saya menambahkan parameter flag ke fungsi pembuat pasangan _p8(s), yang s parameter memberi tahu fungsi apakah akan menambahkan tanda hubung atau tidak.
  • Akhirnya kami membangun GUID dengan perangkaian berikut: _p8() + _p8(true) + _p8(true) + _p8(), dan kembalikan itu.

Tautkan ke posting ini di blog saya

Nikmati! :-)


78



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

Jika ID dihasilkan lebih dari 1 milidetik, mereka 100% unik.

Jika dua ID dihasilkan pada interval yang lebih pendek, dan mengasumsikan bahwa metode acak benar-benar acak, ini akan menghasilkan ID yang 99.99999999999999% kemungkinan secara global unik (tabrakan dalam 1 dari 10 ^ 15)

Anda dapat meningkatkan jumlah ini dengan menambahkan lebih banyak digit, tetapi untuk menghasilkan 100% ID unik, Anda perlu menggunakan penghitung global.

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


68



Berikut ini adalah kombinasi dari atas jawaban yang terpilih, dengan solusi untuk Tabrakan 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);
        });
    };

Di jsbin jika kamu ingin mengujinya.


57