Bagaimana cara membuat GUID / UUID?


4180

Saya mencoba membuat pengidentifikasi unik global dalam JavaScript. Saya tidak yakin rutinitas apa yang tersedia di semua browser, seberapa "acak" dan diunggulkan pembuat bilangan acak bawaan, dll.

GUID / UUID harus memiliki setidaknya 32 karakter dan harus tetap berada dalam kisaran ASCII untuk menghindari masalah ketika melewati mereka.


13
PANDUAN ketika direpresentasikan sebagai string setidaknya 36 dan panjangnya tidak lebih dari 38 karakter dan cocok dengan pola ^ \ {? [A-zA-Z0-9] {36}? \} $ Dan karenanya selalu ascii.
AnthonyWJones

2
David Bau menyediakan generator nomor acak yang jauh lebih baik dan dapat disemai di davidbau.com/archives/2010/01/30/... Saya menulis pendekatan yang sedikit berbeda untuk menghasilkan UUID di blogs.cozi.com/tech/2010/04/generating- uuids-in-javascript.html
George V. Reilly

Aneh bahwa belum ada yang menyebutkan ini tetapi untuk kelengkapan, ada sejumlah besar generator panduan pada npm. Saya berani bertaruh sebagian besar dari mereka juga bekerja di browser.
George Mauer

Jawaban:


2339

UUID (Pengenal Unik Universal), juga dikenal sebagai GUID (Pengenal Unik Global), menurut RFC 4122 , adalah pengidentifikasi yang dirancang untuk memberikan jaminan keunikan tertentu.

Meskipun dimungkinkan untuk mengimplementasikan UUID yang mematuhi RFC dalam beberapa baris JS ( Mis. Lihat jawaban @ broofa , di bawah) ada beberapa perangkap umum:

  • Format id tidak valid (UUID harus dalam bentuk " xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx", di mana x adalah salah satu [0-9, af] M adalah salah satu [1-5], dan N adalah [8, 9, a, atau b]
  • Penggunaan sumber acak berkualitas rendah (seperti Math.random)

Dengan demikian, pengembang yang menulis kode untuk lingkungan produksi didorong untuk menggunakan implementasi yang ketat dan terawat dengan baik seperti modul uuid .


186
Sebenarnya, RFC memungkinkan untuk UUID yang dibuat dari angka acak. Anda hanya perlu memainkan beberapa bit untuk mengidentifikasinya. Lihat bagian 4.4. Algoritma untuk Membuat UUID dari Bilangan Benar-Benar Acak atau Pseudo-Acak: rfc-archive.org/getrfc.php?rfc=4122
Jason DeFontes

Membangun semua yang ada di utas ini, saya membuat versi yang dua kali lebih cepat dari varian "e7" di bawah ini, crypto-strong, dan bekerja pada semua browser dan node utama. Terlalu besar untuk dimasukkan di sini, jadi cari jawaban baru dengan nama saya pada 17 Mei 2020.
Bennett Barouch

node-uuid kini menjadi uuid (setelah bergabung dengan proyek yang terakhir)
Catweazle

4115

Untuk solusi yang sesuai dengan versi 4 RFC4122 , 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());

Pembaruan, 2015-06-02 : Perlu diketahui bahwa keunikan UUID sangat bergantung pada generator nomor acak (RNG). Solusi atas penggunaan Math.random()untuk singkatnya, namun Math.random()ini tidak dijamin untuk menjadi RNG berkualitas tinggi. Lihat langganan Adam Hyland yang bagus di Math.random () untuk detailnya. Untuk solusi yang lebih kuat, pertimbangkan untuk menggunakan modul uuid , yang menggunakan API RNG dengan kualitas lebih tinggi.

Pembaruan, 2015-08-26 : Sebagai catatan tambahan, inti ini menjelaskan cara menentukan berapa banyak ID yang dapat dihasilkan sebelum mencapai probabilitas tabrakan tertentu. Misalnya, dengan 3.26x10 15 versi 4 RFC4122 UUID Anda memiliki peluang tabrakan 1-dalam-sejuta.

Pembaruan, 2017-06-28 : Artikel bagus dari pengembang Chrome yang membahas kondisi kualitas PRNG Math.random di Chrome, Firefox, dan Safari. tl; dr - Pada akhir 2015 ini "cukup bagus", tetapi tidak berkualitas kriptografis. Untuk mengatasi masalah itu, berikut ini adalah versi terbaru dari solusi di atas yang menggunakan ES6, cryptoAPI, dan sedikit sihir JS yang tidak dapat saya setujui 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());

Pembaruan, 2020-01-06 : Ada proposal dalam karya untuk uuidmodul standar sebagai bagian dari bahasa JS


30
Saya memposting pertanyaan tentang collision stackoverflow.com/questions/6906916/…
Muxa

4
@marc - Kualitas Math.random () menjadi perhatian. Tetapi tanpa analisis terperinci dari implementasi yang mendasarinya, yang hampir pasti bervariasi x-browser, kita tidak bisa mengetahui peluang tabrakan yang sebenarnya. Jadi untuk kesederhanaan, saya mengasumsikan sumber keacakan yang ideal. Tapi, ya, itu bisa menjadi asumsi berbahaya ketika masalah muxa disorot. Itu juga mengapa di simpul-uuid ( github.com/broofa/node-uuid ) saya lebih suka API lain yang menjamin keacakan kualitas kriptografi di atas Math.random (), meskipun kinerja menderita karenanya.
broofa

144
Tentunya jawaban untuk pertanyaan @ Muxa adalah 'tidak'? Tidak pernah benar-benar aman untuk memercayai sesuatu yang datang dari klien. Saya kira itu tergantung pada seberapa besar kemungkinan pengguna Anda membuka konsol javascript dan secara manual mengubah variabel menjadi sesuatu yang mereka inginkan. Atau mereka bisa saja POST Anda kembali id ​​yang mereka inginkan. Itu juga akan tergantung pada apakah pengguna memilih ID mereka sendiri akan menyebabkan kerentanan. Either way, jika itu adalah nomor acak ID yang akan menjadi tabel, saya mungkin akan membuatnya di sisi server, sehingga saya tahu saya memiliki kontrol atas proses.
Cam Jackson

36
@DrewNoakes - UUID bukan hanya serangkaian # yang sepenuhnya acak. "4" adalah versi uuid (4 = "acak"). "Y" menandai di mana varian uuid (tata letak bidang, pada dasarnya) perlu disematkan. Lihat bagian 4.1.1 dan 4.1.3 dari ietf.org/rfc/rfc4122.txt untuk info lebih lanjut.
broofa

5
mengapa c== 'x'bukannya c === 'x'. Karena jshint gagal.
Fizer Khan

811

Saya sangat suka betapa bersihnya jawaban Broofa , tetapi sangat disayangkan implementasi yang buruk dariMath.random meninggalkan kesempatan untuk tabrakan.

Berikut ini adalah solusi yang sesuai dengan RFC4122 versi 4 yang memecahkan masalah itu dengan mengimbangi 13 angka hex pertama dengan bagian hex stempel waktu, dan setelah offset habis oleh sebagian hex mikrodetik sejak pageload. Dengan begitu, bahkan jika Math.randomberada pada seed yang sama, kedua klien harus menghasilkan UUID jumlah mikrodetik yang sama persis sejak pageload (jika waktu kinerja tinggi didukung) DAN pada milidetik yang sama persis (atau 10.000+ tahun kemudian) untuk dapatkan UUID yang sama:

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


Ini biola untuk diuji.


31
Ingatlah, new Date().getTime()tidak diperbarui setiap milidetik. Saya tidak yakin bagaimana ini memengaruhi keacakan yang diharapkan dari algoritma Anda.
devios1

84
kinerja. sekarang akan lebih baik. Tidak seperti Date.now, cap waktu yang dikembalikan performance.now()tidak terbatas pada resolusi satu milidetik. Sebaliknya, mereka mewakili waktu sebagai angka floating-point dengan presisi hingga mikrodetik . Juga tidak seperti Date.now, nilai-nilai yang dikembalikan oleh performance.now () selalu meningkat pada laju yang konstan , terlepas dari jam sistem yang dapat disesuaikan secara manual atau condong oleh perangkat lunak seperti Network Time Protocol.
daniellmb

6
@daniellmb Anda mungkin harus menautkan ke MDN atau yang lain untuk menunjukkan dokumentasi nyata dan bukan polyfill;)
Martin

2
Bolehkah saya tahu apa gunanya pembulatan d = Math.floor(d/16);?
Praveen

2
@Praveen Operasi itu menggeser timestamp satu hex-digit ke kanan dan menjatuhkan sisanya. Tujuannya adalah untuk menghilangkan hex digit yang baru saja kita gunakan (yang paling signifikan) dan siap untuk iterasi berikutnya.
Briguy37

431

Jawaban broofa cukup apik, memang - sangat pintar, benar-benar ... rfc4122 sesuai, agak mudah dibaca, dan kompak. Luar biasa!

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

Tapi, bisakah kita mendapatkan kecepatan dan kepatuhan RFC? Saya katakan, YA! Bisakah kita mempertahankan keterbacaan? Ya ... Tidak juga, tetapi mudah jika Anda mengikuti.

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

Jadi dengan iterasi keenam saya tentang optimisasi, saya mengalahkan jawaban paling populer dengan lebih dari 12X , jawaban yang diterima lebih dari 9X , dan jawaban cepat-tidak-sesuai dengan 2-3X . Dan saya masih rfc4122 compliant.

Tertarik bagaimana? Saya telah memasukkan sumber lengkapnya di http://jsfiddle.net/jcward/7hyaC/3/ dan di http://jsperf.com/uuid-generator-opt/4

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

Jadi itu menggantikan xdengan digit hex acak, ydengan data acak (kecuali memaksa 2 bit teratas 10sesuai dengan spesifikasi RFC), dan regex tidak cocok dengan karakter -atau 4, jadi dia tidak harus berurusan dengan mereka. Sangat, sangat apik.

Hal pertama yang perlu diketahui adalah bahwa panggilan fungsi itu mahal, seperti halnya ekspresi reguler (meskipun ia hanya menggunakan 1, ia memiliki 32 panggilan balik, satu untuk setiap pertandingan, dan di masing-masing 32 panggilan balik itu disebut Math.random () dan v. toString (16)).

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

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

Pada dasarnya, logika batin yang sama, kecuali kami memeriksa -atau 4, dan menggunakan loop sementara (bukanreplace() panggilan balik) memberi kami peningkatan hampir 3X!

Langkah selanjutnya adalah langkah kecil di desktop tetapi membuat perbedaan yang layak di ponsel. Mari kita membuat lebih sedikit panggilan Math.random () dan memanfaatkan semua bit acak itu alih-alih membuang 87% dari mereka dengan buffer acak yang digeser keluar setiap iterasi. Mari kita juga memindahkan definisi template dari loop, untuk berjaga-jaga jika 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
}

console.log(e2())

Ini menghemat 10-30% tergantung pada platform. Tidak buruk. Tetapi langkah besar selanjutnya menghilangkan panggilan fungsi toString bersamaan dengan optimasi klasik - tabel pencarian. Tabel pencarian 16-elemen sederhana akan melakukan pekerjaan toString (16) dalam waktu yang jauh lebih singkat:

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

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

console.log(e5())

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

Teknik optimalisasi akhir untuk diterapkan - membuka gulungan lingkaran. Karena kita mengulang-ulang beberapa kali, kita secara teknis dapat menulis ini dengan tangan. Saya mencoba ini sekali dengan variabel acak tunggal r yang saya terus menetapkan ulang, dan kinerja mabuk. Tetapi dengan empat variabel ditugaskan data acak di depan, kemudian menggunakan tabel pencarian, dan menerapkan bit RFC yang tepat, versi ini merokok semuanya:

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

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

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

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

Maklum: tujuan utama saya adalah menunjukkan dan mengajarkan strategi optimasi yang potensial. Jawaban lain mencakup topik-topik penting seperti tabrakan dan angka acak, yang penting untuk menghasilkan UUID yang baik.


14
Kode ini masih mengandung beberapa kesalahan: Math.random()*0xFFFFFFFFbaris harus Math.random()*0x100000000untuk keacakan penuh, dan >>>0harus digunakan alih-alih |0untuk menjaga nilai-nilai tidak ditandatangani (meskipun dengan kode saat ini saya pikir itu akan baik-baik saja meskipun sudah ditandatangani). Akhirnya akan menjadi ide yang sangat bagus hari ini untuk digunakan window.crypto.getRandomValuesjika tersedia, dan kembali ke Math.random hanya jika benar-benar diperlukan. Math.random mungkin memiliki kurang dari 128 bit entropi, dalam hal ini akan lebih rentan terhadap benturan daripada yang diperlukan.
Dave

Membangun semua yang sudah ada di utas ini, saya telah membangun sesuatu dua kali lebih cepat dari "e7", portable semua lingkungan, termasuk node, dan ditingkatkan dari Math.random () ke keacakan kekuatan crypto. Anda mungkin tidak berpikir uuid membutuhkan kekuatan crypto, tetapi apa artinya itu adalah kesempatan tabrakan yang lebih kecil, yang merupakan keseluruhan poin dari uuid. Terlalu besar untuk dimasukkan ke dalam komentar, saya telah mempostingnya secara terpisah.
Bennett Barouch

164

Berikut adalah beberapa kode berdasarkan RFC 4122 , bagian 4.4 (Algoritma untuk Membuat UUID dari Truly Random atau Pseudo-Random Number).

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
Anda harus mendeklarasikan ukuran array sebelumnya daripada mengukurnya secara dinamis saat Anda membangun GUID. var s = new Array(36);
MgSam

1
Saya pikir ada bug yang sangat kecil di baris yang menetapkan bit 6-7 dari clock_seq_hi_and_reserved ke 01. Karena s [19] adalah karakter '0' .. 'f' dan bukan int 0x0..0xf, (s [19] & 0x3) | 0x8 tidak akan didistribusikan secara acak - itu akan cenderung menghasilkan lebih banyak '9's dan lebih sedikit' b's. Ini hanya membuat perbedaan jika Anda peduli tentang distribusi acak karena suatu alasan.
John Velonis

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

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

Jika dua ID dihasilkan dengan interval yang lebih pendek, dan dengan asumsi bahwa metode acak benar-benar acak, ini akan menghasilkan ID yang 99,9999999999999999% cenderung unik secara global (tabrakan dalam 1 dari 10 ^ 15)

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

jika Anda memerlukan kompatibilitas RFC, format ini akan lulus sebagai GUID versi 4 yang valid:

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

Sunting: Kode di atas mengikuti niat, tetapi bukan huruf RFC. Di antara perbedaan lainnya adalah beberapa angka acak pendek. (Tambahkan lebih banyak angka acak jika Anda membutuhkannya) Sisi baiknya adalah ini sangat cepat :) Anda dapat menguji validitas GUID Anda di sini


4
Ini bukan UUID?
Marco Kerwitz

Tidak. UUID / GUID adalah nomor 122 bit (+ enam bit yang dipesan). itu mungkin menjamin keunikan melalui layanan konter global, tetapi seringkali itu menyampaikan tentang waktu, alamat MAC dan keacakan. UUID tidak acak! UID yang saya sarankan di sini tidak sepenuhnya terkompresi. Anda bisa mengompresnya, ke integer 122 bit, menambahkan 6 bit yang telah ditentukan dan bit acak tambahan (menghapus beberapa bit timer) dan Anda berakhir dengan UUID / GUID yang terbentuk sempurna, yang kemudian harus Anda konversikan ke hex. Bagi saya itu tidak benar-benar menambahkan apa pun selain kepatuhan pada panjang ID.
Simon Rigét

5
Menyampaikan alamat MAC untuk keunikan pada mesin virtual adalah ide yang buruk!
Simon Rigét

1
Saya melakukan sesuatu seperti ini, tetapi dengan karakter terkemuka dan beberapa tanda hubung (misalnya [slug, date, random].join("_")untuk membuat usr_1dcn27itd_hj6onj6phr. Itu membuatnya id juga berfungsi sebagai bidang "dibuat pada"
Seph Reed

95

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

Sepuluh juta eksekusi dari implementasi ini hanya membutuhkan 32,5 detik, yang merupakan yang 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.
 * @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 kinerja, 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 perlu penjelasan:

Algoritma:

  • The Math.random()mengembalikan fungsi angka desimal antara 0 dan 1 dengan 16 digit setelah titik pecahan desimal (misalnya 0.4363923368509859).
  • Kemudian kita mengambil nomor ini dan mengubahnya menjadi string dengan basis 16 (dari contoh di atas kita akan dapatkan 0.6fb7687f).
    Math.random().toString(16).
  • Lalu kami memotong 0.awalan ( 0.6fb7687f=> 6fb7687f) dan mendapatkan string dengan delapan karakter heksadesimal.
    (Math.random().toString(16).substr(2,8).
  • Kadang-kadang Math.random()fungsi akan mengembalikan angka yang lebih pendek (misalnya 0.4363), karena nol di akhir (dari contoh di atas, sebenarnya angkanya 0.4363000000000000). Itu sebabnya saya menambahkan string ini "000000000"(string dengan sembilan nol) dan kemudian memotongnya dengan substr()fungsi untuk membuatnya menjadi sembilan karakter persis (mengisi nol di sebelah kanan).
  • Alasan untuk menambahkan tepat sembilan nol adalah karena skenario kasus yang lebih buruk, yaitu ketika Math.random()fungsi akan mengembalikan tepat 0 atau 1 (probabilitas 1/10 ^ 16 untuk masing-masing). Itu sebabnya kami perlu menambahkan sembilan nol padanya ( "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 membahayakan hasilnya karena tetap memotongnya.
    Math.random().toString(16)+"000000000").substr(2,8).

Perakitan:

  • GUID dalam format berikut XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.
  • Saya membagi GUID menjadi 4 bagian, masing-masing bagian dibagi menjadi 2 jenis (atau format): XXXXXXXXdan -XXXX-XXXX.
  • Sekarang saya sedang membangun GUID menggunakan 2 jenis ini untuk merakit GUID dengan panggilan 4 buah, sebagai berikut: XXXXXXXX -XXXX-XXXX -XXXX-XXXX XXXXXXXX.
  • Untuk berbeda di antara kedua jenis ini, saya menambahkan parameter flag ke fungsi pencipta pasangan _p8(s), sparameter memberitahu fungsi apakah akan menambahkan tanda hubung atau tidak.
  • Akhirnya kami membangun GUID dengan rangkaian berikut:, _p8() + _p8(true) + _p8(true) + _p8()dan mengembalikannya.

Tautkan ke posting ini di blog saya

Nikmati! :-)


13
Implementasi ini salah. Karakter tertentu dari GUID memerlukan perlakuan khusus (mis. Digit ke-13 harus menjadi nomor 4).
JLRishe

67

Berikut adalah kombinasi dari jawaban terpilih teratas , 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
        // /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);
        });
    };

Di jsbin jika Anda ingin mengujinya.


3
perhatikan bahwa versi pertama, yang `window.crypto.getRandomValues , does not keep the Version 4 UUIDs format defined by RFC 4122. That is instead of xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx` yang dihasilkannya xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
humanityANDpeace

66

Berikut ini adalah implementasi yang benar-benar tidak sesuai tetapi sangat berkinerja untuk menghasilkan pengidentifikasi unik seperti GUID yang aman seperti ASCII.

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

Menghasilkan 26 [a-z0-9] karakter, menghasilkan UID yang lebih pendek dan lebih unik daripada GUID yang mematuhi RFC. Tanda hubung dapat ditambahkan dengan sepele jika hal-hal yang mudah dibaca oleh manusia.

Berikut adalah contoh penggunaan dan pengaturan waktu untuk fungsi ini dan beberapa jawaban lain dari pertanyaan ini. Waktunya dilakukan di bawah Chrome m25, masing-masing 10 juta iterasi.

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

Ini kode waktu.

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

62

Berikut adalah solusi tertanggal 9 Oktober 2011 dari komentar oleh pengguna jed di 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)}

Ini mencapai tujuan yang sama dengan jawaban berperingkat tertinggi saat ini , tetapi dalam 50+ lebih sedikit byte dengan mengeksploitasi paksaan, rekursi, dan notasi eksponensial. Bagi mereka yang penasaran bagaimana cara kerjanya, berikut adalah bentuk dari versi yang lebih tua dari fungsi:

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

Dari blog teknis 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;
}

Ada metode lain yang melibatkan menggunakan kontrol ActiveX, tetapi menjauhlah dari ini!

Sunting: Saya pikir perlu menunjukkan bahwa tidak ada generator GUID yang dapat menjamin kunci unik (lihat artikel wikipedia ). Selalu ada kemungkinan tabrakan. GUID hanya menawarkan semesta kunci yang cukup besar untuk mengurangi perubahan tabrakan menjadi hampir nol.


8
Perhatikan bahwa ini bukan GUID dalam arti teknis, karena tidak melakukan apa pun untuk menjamin keunikan. Itu mungkin atau mungkin tidak masalah tergantung pada aplikasi Anda.
Stephen Deken

2
Catatan singkat tentang kinerja. Solusi ini menciptakan total 36 string untuk mendapatkan hasil tunggal. Jika kinerjanya sangat penting, pertimbangkan untuk membuat array dan bergabung seperti yang direkomendasikan oleh: tinyurl.com/y37xtx Penelitian lebih lanjut menunjukkan bahwa itu tidak masalah, jadi YMMV: tinyurl.com/3l7945
Brandon DuRette

2
Mengenai keunikan, perlu dicatat bahwa versi 1.3, dan 5 UUID bersifat deterministik seperti versi 4. Jika input ke generator uuid ini - id simpul di v1, namespace dan nama di v3 dan v5 - unik (sebagaimana seharusnya), maka UUID yang dihasilkan menjadi unik. Secara teori, sih.
broofa

41

Anda dapat menggunakan simpul-uuid ( https://github.com/kelektiv/node-uuid )

Sederhana, pembuatan cepat RFC4122 UUIDS.

Fitur:

  • Hasilkan RID4122 versi 1 atau versi 4 UUID
  • Berjalan di node.js dan browser.
  • Pembuatan acak # yang kuat secara cryptografis pada platform pendukung.
  • Jejak kecil (Ingin yang lebih kecil? Lihat ini! )

Instal Menggunakan NPM:

npm install uuid

Atau Menggunakan uuid melalui browser:

Unduh Raw File (uuid v1): https://raw.githubusercontent.com/kelektiv/node-uuid/master/v1.js Unduh Raw File (uuid v4): https://raw.githubusercontent.com/kelektiv/node -uuid / master / v4.js


Ingin lebih kecil? Lihat ini: https://gist.github.com/jed/982883


Pemakaian:

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

EDIT:

Tinjau kembali proyek saya yang menggunakan fungsi ini dan tidak menyukai verbositasnya. - Tapi perlu keacakan yang tepat.

Versi yang didasarkan pada jawaban Briguy37 dan beberapa operator bitwise untuk mengekstrak jendela berukuran menggigit dari buffer.

Harus mematuhi skema RFC Type 4 (acak), karena saya memiliki Masalah terakhir kali parsing uuids tidak sesuai dengan UUID Jawa.


31

Modul JavaScript sederhana sebagai kombinasi jawaban terbaik di utas ini.

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

Pemakaian:

Guid.newGuid ()

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

Guid.empty

"00000000-0000-0000-0000-000000000000"


1
Yang mengganggu tentang semua jawaban adalah tampaknya tidak apa - apa jika JavaScript menyimpannya GUIDsebagai string. Jawaban Anda setidaknya menangani penyimpanan yang jauh lebih efisien menggunakan a Uint16Array. The toStringFungsi harus menggunakan representasi biner dalam JavaScriptobject
Sebastian

UUID yang dihasilkan oleh kode ini lemah-tapi-sesuai-RFC (_guid), atau kuat-tapi-bukan-sesuai-RFC (_cryptoGuid). Yang pertama menggunakan Math.random (), yang sekarang dikenal sebagai RNG yang buruk. Yang terakhir gagal untuk mengatur bidang versi dan varian.
broofa

@broofa - Apa yang Anda sarankan untuk membuatnya kuat dan sesuai dengan RFC? Dan mengapa _cryptoGuid tidak sesuai dengan RFC?
Matt

@Mat _cryptoGuid () menyetel semua 128 bit secara acak, artinya ia tidak mengatur bidang versi dan varian seperti yang dijelaskan dalam RFC. Lihat implementasi alternatif uuidv4 () yang menggunakan crypto.getRandomValues ​​() di jawaban pilihan saya di atas, untuk implementasi yang sesuai + kuat.
broofa

29

Ini membuat UUID versi 4 (dibuat dari angka acak semu):

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

Berikut adalah contoh UUID yang dihasilkan:

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

28

Yah, ini sudah memiliki banyak jawaban, tetapi sayangnya tidak ada acak "benar" dalam kelompok itu. Versi di bawah ini merupakan adaptasi dari jawaban broofa, tetapi diperbarui untuk menyertakan fungsi acak "benar" yang menggunakan pustaka crypto di mana tersedia, dan fungsi Alea () sebagai fallback.

  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

Proyek JavaScript di GitHub - https://github.com/LiosK/UUID.js

UUID.js Generator UUID yang mematuhi RFC untuk JavaScript.

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

Fitur Menghasilkan RID 4122 yang sesuai dengan UUID.

Versi 4 UUID (UUID dari nomor acak) dan UUID versi 1 (UUID berbasis waktu) tersedia.

Objek UUID memungkinkan berbagai akses ke UUID termasuk akses ke bidang UUID.

Resolusi timestamp rendah JavaScript dikompensasi dengan angka acak.


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

Saya ingin memahami jawaban broofa, jadi saya mengembangkannya dan menambahkan komentar:

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

Terima kasih atas uraian terperinci! Khususnya menggigit dikurung antara 8 dan 11 dengan penjelasan yang setara sangat membantu.
Egor Litvinchuk

15

Menyesuaikan generator UUID / GUID saya sendiri dengan beberapa tambahan di sini .

Saya menggunakan Kybos berikut generator nomor acak untuk menjadi sedikit lebih kriptografis suara.

Di bawah ini adalah skrip saya dengan metode Mash dan Kybos dari baagoe.com dikecualikan.

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

Bagi mereka yang menginginkan solusi yang sesuai versi rfc4122 dengan pertimbangan kecepatan (beberapa panggilan ke 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() );

Fungsi di atas harus memiliki keseimbangan yang layak antara kecepatan dan keacakan.


13

Sampel 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

Cara yang lebih baik:

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
 }

Diperkecil:

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

Saya tahu, ini pertanyaan lama. Hanya untuk kelengkapan, jika lingkungan Anda adalah SharePoint, ada fungsi utilitas yang disebut SP.Guid.newGuid( tautan msdn ) yang membuat panduan baru. Fungsi ini ada di dalam file sp.init.js. Jika Anda menulis ulang fungsi ini (untuk menghapus beberapa dependensi lainnya dari fungsi pribadi lainnya), sepertinya ini:

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

Yang ini didasarkan pada tanggal, dan menambahkan akhiran acak untuk "memastikan" keunikan. Bekerja dengan baik untuk pengidentifikasi css. Selalu mengembalikan sesuatu seperti dan mudah diretas:

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

Kode sederhana yang digunakan crypto.getRandomValues(a)pada browser yang didukung (IE11 +, iOS7 +, FF21 +, Chrome, Android Chrome). Hindari penggunaan Math.random()karena dapat menyebabkan tabrakan (misalnya 20 tabrakan untuk 4000 uuids yang dihasilkan dalam situasi nyata oleh 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);
}

Catatan:

  • Dioptimalkan untuk pembacaan kode bukan kecepatan, jadi cocok untuk mengatakan beberapa ratus uuid per detik. Menghasilkan sekitar 10.000 uuid () per detik di Chromium di laptop saya menggunakan http://jsbin.com/fuwigo/1 untuk mengukur kinerja.
  • Hanya menggunakan 8 untuk "y" karena itu menyederhanakan pembacaan kode (y diizinkan menjadi 8, 9, A atau B).

11

Jika Anda hanya membutuhkan string 128 bit acak tanpa format tertentu, Anda dapat menggunakan:

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

Yang akan mengembalikan sesuatu seperti 2350143528-4164020887-938913176-2513998651.


BTW, mengapa hanya menghasilkan angka dan bukan karakter? jauh lebih tidak aman
vsync

1
Anda juga dapat menambahkan karakter (huruf) seperti ini:Array.from((window.crypto || window.msCrypto).getRandomValues(new Uint32Array(4))).map(n => n.toString(16)).join('-')
magikMaker

11

Hanya varian lain yang lebih mudah dibaca dengan hanya dua mutasi.

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

Yah sebagian besar pengembang js adalah pengembang web, dan kami tidak akan mengerti apa yang dilakukan operator bitwise, karena kami tidak menggunakannya sebagian besar waktu yang kami kembangkan. Sebenarnya saya tidak pernah membutuhkan satupun dari mereka, dan saya adalah seorang js dev sejak '97. Jadi kode contoh Anda masih benar-benar tidak dapat dibaca oleh pengembang web rata-rata yang akan membacanya. Belum lagi Anda masih menggunakan nama variabel satu huruf, yang membuatnya semakin samar. Mungkin membaca Kode Bersih, mungkin itu membantu: amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/…
inf3rno

@ inf3rno jangan menampar dia, semua solusi yang diusulkan di utas ini samar tetapi mereka jawaban yang benar mengingat pertanyaannya adalah untuk memiliki satu-baris macam. itulah yang satu kalimat samar. mereka tidak mampu dibaca oleh pengembang rata-rata tetapi mereka menyimpan layar real estat di mana komentar sebelumnya yang sederhana akan dilakukan. Dan sebagai hasilnya, akhirnya menjadi jauh lebih mudah dibaca seperti itu jika itu dalam "kode yang dapat dibaca" sebagai gantinya.
tatsu

Acak! = Unik
user1529413

@ user1529413 Ya. Keunikan membutuhkan indeks.
ceving

Ini adalah jawaban favorit saya, karena ia membangun UUID sebagai nilai 16-byte (128 bit), dan bukan bentuknya yang bersambung dan enak dibaca. Ini akan mudah untuk menjatuhkan hal-hal string dan hanya mengatur bit yang benar dari 128bit acak, yang semua uuidv4 perlu. Anda dapat mendasarkannya untuk URL yang lebih pendek, mengirimkannya kembali ke beberapa webassembly, menyimpannya dalam ruang memori lebih sedikit daripada sebagai string, menjadikannya buffer ukuran 4096 dan memasukkan 256 uuids di dalamnya, menyimpannya di browser db, dll. Jauh lebih baik daripada memiliki semuanya sebagai panjang, string hex-encoded huruf kecil dari awal.
Josh dari Qaribou

8

OK, menggunakan paket uuid , mendukung untuk UUID versi 1, 3, 4 dan 5 :

yarn add uuid

lalu:

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

Anda juga dapat melakukannya dengan opsi yang ditentukan sepenuhnya:

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'

Untuk info lebih lanjut, kunjungi halaman npm di sini


6

Adalah penting bahwa untuk menggunakan kode yang teruji dengan baik yang dikelola oleh lebih dari 1 kontributor alih-alih mencambuk barang-barang Anda sendiri untuk ini. Ini adalah salah satu tempat di mana Anda mungkin ingin lebih suka kode yang paling stabil daripada versi pandai yang sesingkat mungkin yang bekerja di browser X tetapi tidak memperhitungkan keanehan akun Y yang sering menyebabkan sangat sulit untuk menyelidiki bug daripada memanifestasikan hanya secara acak untuk beberapa pengguna. Secara pribadi saya menggunakan uuid-js di https://github.com/aurigadl/uuid-js yang mengaktifkan bower agar saya dapat mengambil pembaruan dengan mudah.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.