ค้นหาเว็บไซต์

javascript - ฉันจะสร้าง GUID/UUID ได้อย่างไร?


ฉันจะสร้าง GUIDs (ตัวระบุที่ไม่ซ้ำกันทั่วโลก) ใน JavaScript ได้อย่างไร? GUID/UUID ควรมีความยาวอย่างน้อย 32 ตัวอักษรและควรอยู่ในช่วง ASCII เพื่อหลีกเลี่ยงปัญหาเมื่อต้องส่งต่อ.

ฉันไม่แน่ใจว่าแต่ละเบราว์เซอร์มีฟังก์ชันอะไรบ้าง ตัวสร้างตัวเลขสุ่มในตัวมีความ 'สุ่ม' และใช้เมล็ดข้อมูลอย่างไร เป็นต้น

คำตอบ #1

[แก้ไขวันที่ 2023-03-05 เพื่อสะท้อนแนวปฏิบัติที่ดีที่สุดล่าสุดสำหรับการสร้าง UUID ที่เป็นไปตาม RFC4122]

crypto.randomUUID() ตอนนี้เป็นมาตรฐานในเบราว์เซอร์และสภาพแวดล้อมการรัน JS ทุกสมัยใหม่แล้ว อย่างไรก็ตาม เนื่องจาก API ของเบราว์เซอร์ใหม่ถูกจำกัดให้ใช้ในบริบทที่ปลอดภัย วิธีนี้จึงสามารถใช้ได้เฉพาะกับหน้าเว็บที่ให้บริการในเครื่อง (localhost หรือ 127.0.0.1) หรือผ่าน HTTPS

สำหรับผู้อ่านที่สนใจ UUID เวอร์ชันอื่น ๆ การสร้าง UUID บนแพลตฟอร์มเก่าหรือในบริบทที่ไม่ปลอดภัย มีโมดูล uuid ซึ่งผ่านการทดสอบและได้รับการสนับสนุนเป็นอย่างดี

หากวิธีข้างต้นล้มเหลว มีวิธีนี้ (อิงจากคำตอบดั้งเดิมของคำถามนี้):

function uuidv4() {
  return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, c =>
    (+c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> +c / 4).toString(16)
  );
}

console.log(uuidv4());

หมายเหตุ: การใช้ UUID generator ใด ๆ ที่อาศัย Math.random() นั้นไม่แนะนำอย่างยิ่ง (รวมถึงโค้ดตัวอย่างที่ปรากฏในเวอร์ชันก่อนหน้าของคำตอบนี้) ด้วยเหตุผลที่อธิบายได้ดีที่สุดที่นี่ สรุปสั้น ๆ: วิธีการที่อิง Math.random() ไม่ให้การรับประกันความเป็นเอกลักษณ์ที่ดี

คำตอบ #2

UUIDs (รหัสระบุเอกลักษณ์สากล), ซึ่งรู้จักกันในชื่อ GUIDs (รหัสระบุเอกลักษณ์ทั่วโลก), ตามมาตรฐาน RFC 4122, เป็นตัวระบุที่ออกแบบมาเพื่อให้มีความเอกลักษณ์บางประการ.

แม้ว่าจะสามารถใช้งาน UUID ที่สอดคล้องกับ RFC ได้ด้วยโค้ด JavaScript เพียงไม่กี่บรรทัด (เช่น ดูคำตอบของ @broofa ด้านล่าง) แต่ก็มีข้อผิดพลาดทั่วไปหลายประการ:

  • รูปแบบไอดีไม่ถูกต้อง (UUID ต้องอยู่ในรูปแบบ "xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx" โดยที่ x เป็นตัวใดตัวหนึ่งใน [0-9, a-f] M เป็นตัวใดตัวหนึ่งใน [1-5] และ N เป็น [8, 9, a, หรือ b]
  • การใช้แหล่งความสุ่มคุณภาพต่ำ (เช่น Math.random)

ดังนั้น นักพัฒนาที่เขียนโค้ดสำหรับสภาพแวดล้อมการผลิตจึงถูกแนะนำให้ใช้การใช้งานที่เข้มงวดและได้รับการดูแลอย่างดี เช่น โมดูล uuid

คำตอบ #3

ฉันชอบคำตอบของ Broofa ที่สะอาดมากจริง ๆ แต่เป็นเรื่องน่าเสียดายที่การใช้งาน Math.random อย่างไม่ดีทำให้มีโอกาสเกิดการชนกัน

นี่คือตัวอย่างโซลูชันที่สอดคล้องกับ RFC4122 เวอร์ชัน 4 ที่คล้ายกัน ซึ่งแก้ปัญหานั้นโดยการเลื่อนเลขฐานสิบหก 13 หลักแรกด้วยส่วนฐานสิบหกของเวลาประทับ (timestamp) และเมื่อใช้หมดแล้วจะเลื่อนด้วยส่วนฐานสิบหกของไมโครวินาทีตั้งแต่โหลดหน้าเว็บ วิธีนี้ แม้ว่า Math.random จะอยู่บนค่า seed เดียวกัน ทั้งสองไคลเอนต์ก็จะต้องสร้าง UUID ในไมโครวินาทีเดียวกันนับตั้งแต่โหลดหน้าเว็บ (ถ้ารองรับเวลาประสิทธิภาพสูง) และในมิลลิวินาทีเดียวกันพอดี (หรือ 10,000+ ปีต่อมา) ถึงจะได้ UUID เดียวกัน:

function generateUUID() { // Public Domain/MIT
    var d = new Date().getTime();//Timestamp
    var d2 = ((typeof performance !== 'undefined') && 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);
    });
}

var onClick = function(){
    document.getElementById('uuid').textContent = generateUUID();
}
onClick();
#uuid { font-family: monospace; font-size: 1.5em; }
<p id="uuid"></p>
<button id="generateUUID" onclick="onClick();">Generate UUID</button>

นี่คือฟิดเดิลสำหรับทดสอบ


โค้ดย่อยที่ปรับปรุงให้ทันสมัยสำหรับ ES6

const generateUUID = () => {
  let
    d = new Date().getTime(),
    d2 = ((typeof performance !== 'undefined') && performance.now && (performance.now() * 1000)) || 0;
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
    let r = Math.random() * 16;
    if (d > 0) {
      r = (d + r) % 16 | 0;
      d = Math.floor(d / 16);
    } else {
      r = (d2 + r) % 16 | 0;
      d2 = Math.floor(d2 / 16);
    }
    return (c == 'x' ? r : (r & 0x7 | 0x8)).toString(16);
  });
};

const onClick = (e) => document.getElementById('uuid').textContent = generateUUID();

document.getElementById('generateUUID').addEventListener('click', onClick);

onClick();
#uuid { font-family: monospace; font-size: 1.5em; }
<p id="uuid"></p>
<button id="generateUUID">Generate UUID</button>

คำตอบ #4

คำตอบของ broofa นั้นเจ๋งมาก จริง ๆ แล้วฉลาดน่าประทับใจ... เป็นไปตามมาตรฐาน RFC4122 อ่านได้พอประมาณ และกะทัดรัด ยอดเยี่ยม!

แต่ถ้าคุณกำลังดูที่นิพจน์ปกตินั้น ฟังก์ชัน callback ของ replace() ที่มีมากมาย toString() และการเรียกใช้ฟังก์ชัน Math.random() (ซึ่งเขาใช้เพียงสี่บิตของผลลัพธ์และทิ้งส่วนที่เหลือ) คุณอาจเริ่มสงสัยเกี่ยวกับประสิทธิภาพจริง ๆ joelpt ยังตัดสินใจที่จะส่ง RFC สำหรับความเร็วของ GUID แบบทั่วไปด้วย generateQuickGUID

แต่เราจะได้ทั้งความเร็ว และ การปฏิบัติตาม RFC ไหม? ผมตอบว่า ได้! เราสามารถรักษาความอ่านง่ายได้ไหม? อืม... ไม่ค่อยได้ แต่ก็ง่ายถ้าคุณตามไปด้วย

แต่ก่อนอื่น ผลลัพธ์ของฉัน เมื่อเปรียบเทียบกับ broofa, guid (คำตอบที่ได้รับการยอมรับ) และ generateQuickGuid ที่ไม่เป็นไปตามมาตรฐาน rfc:

                  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.

ดังนั้นในการปรับแต่งครั้งที่ 6 ของฉัน ฉันสามารถทำได้ดีกว่าคำตอบที่ได้รับความนิยมที่สุดมากกว่า 12 เท่า, ดีกว่าคำตอบที่ได้รับการยอมรับมากกว่า 9 เท่า, และดีกว่าคำตอบที่เร็วแต่ไม่เป็นไปตามมาตรฐานมากกว่า 2-3 เท่า. และฉันยังคงเป็นไปตามมาตรฐาน RFC 4122 อยู่

สนใจยังไง? ฉันได้ใส่ซอร์สโค้ดทั้งหมดไว้ที่ http://jsfiddle.net/jcward/7hyaC/3/ และที่ https://jsben.ch/xczxS

สำหรับคำอธิบาย มาเริ่มต้นด้วยรหัสของ 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())

ดังนั้นมันจะแทนที่ x ด้วยเลขฐานสิบหกแบบสุ่มใดก็ได้, y ด้วยข้อมูลแบบสุ่ม (ยกเว้นบังคับให้สองบิตบนสุดเป็น 10 ตามสเปค RFC), และ regex ไม่ตรงกับอักขระ - หรือ 4 ดังนั้นเขาไม่ต้องจัดการกับมันเลย ลื่นไหลมาก ๆ

สิ่งแรกที่ต้องรู้คือการเรียกฟังก์ชันมีค่าใช้จ่ายสูง เช่นเดียวกับนิพจน์ปกติ (แม้ว่าเขาจะใช้เพียง 1 ตัว แต่มันมี 32 คอลแบ็ค ซึ่งหนึ่งสำหรับแต่ละการจับคู่ และในแต่ละคอลแบ็คทั้ง 32 ตัวนั้นมันเรียกใช้ Math.random() และ v.toString(16))

ขั้นตอนแรกสู่ประสิทธิภาพคือการกำจัด RegEx และฟังก์ชันคอลล์แบ็กของมัน และใช้ลูปธรรมดาแทน ซึ่งหมายความว่าเราต้องจัดการกับอักขระ - และ 4 ในขณะที่ broofa ไม่ต้องทำ นอกจากนี้ โปรดสังเกตว่าเราสามารถใช้การเข้าถึงดัชนีของอาเรย์สตริงเพื่อเก็บสถาปัตยกรรมเทมเพลตสตริงที่ลื่นไหลของเขา:

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

พื้นฐานแล้ว หลักตรรกะภายในเหมือนเดิม ต่างกันตรงที่เราตรวจสอบ - หรือ 4 และการใช้ลูป while (แทนการใช้ callback ของ replace()) ทำให้เราได้ประสิทธิภาพดีขึ้นเกือบ 3 เท่า!

ขั้นตอนต่อไปเป็นขั้นตอนเล็ก ๆ บนเดสก์ท็อป แต่มีความแตกต่างค่อนข้างชัดเจนบนมือถือ มาลดจำนวนครั้งของการเรียก Math.random() และใช้บิตสุ่มทั้งหมดเหล่านั้น แทนที่จะทิ้งไป 87% ด้วยบัฟเฟอร์สุ่มที่ถูกเลื่อนออกไปในแต่ละรอบ นอกจากนี้มาขยับการกำหนดแม่แบบออกจากลูป เผื่อว่าจะช่วยได้:

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

นี่ช่วยประหยัดได้ 10-30% ขึ้นอยู่กับแพลตฟอร์ม ไม่เลวเลย แต่ขั้นตอนใหญ่ถัดไปคือการกำจัดการเรียกใช้ฟังก์ชัน toString ทั้งหมดด้วยการปรับแต่งแบบคลาสสิก - ตารางค้นหา ตารางค้นหาแบบง่าย 16 ตัวจะทำหน้าที่ของ toString(16) ได้ในเวลาที่น้อยกว่ามาก:

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

การเพิ่มประสิทธิภาพถัดไปเป็นอีกหนึ่งวิธีคลาสสิก เนื่องจากเรากำลังจัดการเพียงสี่บิตของเอาต์พุตในแต่ละรอบของลูป ให้เราลดจำนวนรอบลูปลงครึ่งหนึ่งและประมวลผลแปดบิตในแต่ละรอบ ซึ่งซับซ้อนเล็กน้อยเนื่องจากเรายังต้องจัดการกับตำแหน่งบิตที่สอดคล้องกับ RFC แต่ก็ไม่ยากเกินไป จากนั้นเราต้องสร้างตารางค้นหาที่ใหญ่ขึ้น (16x16 หรือ 256) เพื่อเก็บค่า 0x00 - 0xFF และเราสร้างตารางนี้เพียงครั้งเดียว นอกฟังก์ชัน 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())

ฉันได้ลองใช้ e6() ที่ประมวลผล 16 บิตในแต่ละครั้ง โดยยังคงใช้ LUT ขนาด 256 องค์ประกอบอยู่ และมันแสดงให้เห็นถึงผลตอบแทนที่ลดลงจากการปรับแต่ง แม้ว่ามันจะมีการวนซ้ำน้อยกว่า แต่ตรรกะภายในซับซ้อนขึ้นจากการประมวลผลที่เพิ่มขึ้น และมันทำงานเหมือนเดิมบนเดสก์ท็อป และเร็วขึ้นเพียงประมาณ 10% บนมือถือ

เทคนิคการปรับแต่งขั้นสุดท้ายที่ควรใช้ - ขยายลูป เนื่องจากเรากำลังวนลูปจำนวนครั้งที่แน่นอน เราจึงสามารถเขียนทั้งหมดด้วยมือได้ทางเทคนิค ฉันเคยลองทำแบบนี้ครั้งหนึ่งกับตัวแปรสุ่มเพียงตัวเดียว r ที่ฉันเก็บและกำหนดค่าใหม่ และประสิทธิภาพตกลงอย่างมาก แต่ด้วยสี่ตัวแปรที่กำหนดข้อมูลสุ่มล่วงหน้า จากนั้นใช้ตารางค้นหา และใช้บิต RFC ที่เหมาะสม เวอร์ชันนี้ทำงานได้ดีกว่าทุกตัว:

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

แยกเป็นโมดูล: http://jcward.com/UUID.js - UUID.generate()

เรื่องตลกก็คือ การสร้างข้อมูลสุ่ม 16 ไบต์เป็นเรื่องง่าย ส่วนเคล็ดลับทั้งหมดคือการแสดงมันในรูปแบบ สตริง ที่เป็นไปตามมาตรฐาน RFC และสามารถทำได้อย่างมีประสิทธิภาพที่สุดด้วยข้อมูลสุ่ม 16 ไบต์ ลูปที่ไม่ซ้ำ และตารางค้นหา

ฉันหวังว่าตรรกะของฉันถูกต้อง -- มันง่ายมากที่จะทำผิดพลาดในงานชิ้นเล็กที่น่าเบื่อนี้ แต่ผลลัพธ์ดูดีสำหรับฉัน ฉันหวังว่าคุณจะสนุกกับการเดินทางบ้า ๆ นี้ผ่านการปรับแต่งโค้ด!

โปรดทราบ: เป้าหมายหลักของฉันคือการแสดงและสอนกลยุทธ์การปรับแต่งที่เป็นไปได้ คำตอบอื่น ๆ ครอบคลุมหัวข้อสำคัญ เช่น การชนกันและตัวเลขสุ่มจริง ซึ่งมีความสำคัญต่อการสร้าง UUID ที่ดี

คำตอบ #5

ใช้ :

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

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

ถ้า ID ถูกสร้างห่างกันมากกว่า 1 มิลลิวินาที พวกมันจะมีความเป็นเอกลักษณ์ 100%

หากมีการสร้างรหัสประจำตัวสองรหัสในช่วงเวลาที่สั้นลง และสมมติว่าวิธีการสุ่มนั้นเป็นการสุ่มจริง ๆ นี่จะสร้างรหัสประจำตัวที่มีความเป็นไปได้ 99.99999999999999% ที่จะไม่ซ้ำกันทั่วโลก (การชนกันเกิดขึ้น 1 ใน 10^15)

คุณสามารถเพิ่มตัวเลขนี้ได้โดยการเพิ่มตัวเลขมากขึ้น แต่เพื่อสร้างรหัส ID ที่ไม่ซ้ำกัน 100% คุณจะต้องใช้ตัวนับทั่วโลก

หากคุณต้องการความเข้ากันได้กับ RFC การจัดรูปแบบนี้จะถือว่าเป็น GUID เวอร์ชัน 4 ที่ถูกต้อง:

let u = Date.now().toString(16) + Math.random().toString(16) + '0'.repeat(16);
let guid = [u.substr(0,8), u.substr(8,4), '4000-8' + u.substr(13,3), u.substr(16,12)].join('-');

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('-');
document.getElementById("unique").innerHTML = guid;
<div id="unique">
</div>

โค้ดข้างต้นเป็นไปตามเจตนา แต่ไม่ตรงกับตัวอักษรของ RFC ในบรรดาความแตกต่างอื่น ๆ มันขาดตัวเลขสุ่มไม่กี่ตัว (เพิ่มตัวเลขสุ่มมากขึ้นหากคุณต้องการ) ข้อดีคือมันรวดเร็วมาก :) คุณสามารถทดสอบความถูกต้องของ GUID ของคุณที่นี่

คำตอบ #6

นี่คือโค้ดบางส่วนที่อิงตาม RFC 4122, ส่วนที่ 4.4 (อัลกอริทึมสำหรับการสร้าง UUID จากตัวเลขสุ่มแท้หรือสุ่มเทียม)

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

คำตอบ #7

นี่คือวิธีสร้างสตริงที่คล้าย GUID ที่เร็วที่สุดในรูปแบบ XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX ซึ่งไม่ได้สร้าง GUID ตามมาตรฐาน

การประมวลผลการใช้งานครั้งนี้จำนวนสิบล้านครั้งใช้เวลาเพียง 32.5 วินาที ซึ่งเป็นสิ่งที่เร็วที่สุดที่ฉันเคยเห็นในเบราว์เซอร์ (วิธีแก้ปัญหาเดียวที่ไม่มีลูป/การวนซ้ำ)

ฟังก์ชันนั้นง่ายเหมือน:

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

เพื่อทดสอบประสิทธิภาพ คุณสามารถรันโค้ดนี้ได้:

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

ฉันมั่นใจว่าส่วนใหญ่ของพวกคุณจะเข้าใจสิ่งที่ฉันทำที่นั่น แต่บางทีอาจมีอย่างน้อยหนึ่งคนที่ต้องการคำอธิบาย:

อัลกอริทึม:

  • ฟังก์ชัน Math.random() จะคืนค่าตัวเลขทศนิยมระหว่าง 0 และ 1 โดยมี 16 หลักหลังจุดทศนิยม (ตัวอย่างเช่น 0.4363923368509859)
  • จากนั้นเราจะเอาตัวเลขนี้และแปลงเป็นสตริงในฐาน 16 (จากตัวอย่างด้านบนเราจะได้ 0.6fb7687f) Math.random().toString(16)
  • จากนั้นเราตัดคำนำหน้า 0. (0.6fb7687f => 6fb7687f) และจะได้สตริงที่ยาวแปดตัวอักษรฐานสิบหก (Math.random().toString(16).substr(2,8).
  • บางครั้งฟังก์ชัน Math.random() จะคืนค่าตัวเลขที่สั้นกว่า (เช่น 0.4363) เนื่องจากมีเลขศูนย์ต่อท้าย (จากตัวอย่างข้างต้น แท้จริงแล้วตัวเลขคือ 0.4363000000000000) นั่นคือเหตุผลที่ฉันต่อสตริงนี้ด้วย "000000000" (สตริงที่มีเลขศูนย์เก้าตัว) แล้วค่อยตัดออกด้วยฟังก์ชัน substr() เพื่อให้ได้เก้าตัวอักษรพอดี (เติมศูนย์ทางด้านขวา)
  • เหตุผลที่เพิ่มเลขศูนย์ทั้งหมดเก้าตัวคือเพราะสถานการณ์ที่เลวร้ายที่สุด ซึ่งคือเมื่อฟังก์ชัน Math.random() จะคืนค่าเป็น 0 หรือ 1 อย่างแม่นยำ (มีความน่าจะเป็น 1/10^16 สำหรับแต่ละค่าดังกล่าว) นั่นคือเหตุผลที่เราต้องเพิ่มเลขศูนย์เก้าตัวเข้าไป ("0"+"000000000" หรือ "1"+"000000000") จากนั้นทำการตัดจากดัชนีที่สอง (ตัวอักษรที่สาม) ด้วยความยาวแปดตัวอักษร สำหรับกรณีอื่น การเพิ่มเลขศูนย์จะไม่ทำให้ผลลัพธ์เสียหายเพราะจะถูกตัดอยู่ดี Math.random().toString(16)+"000000000").substr(2,8)

การชุมนุม:

  • GUID อยู่ในรูปแบบดังต่อไปนี้ XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.
  • ฉันแบ่ง GUID ออกเป็นสี่ชิ้น แต่ละชิ้นแบ่งออกเป็นสองประเภท (หรือรูปแบบ): XXXXXXXX และ -XXXX-XXXX
  • ตอนนี้ฉันกำลังสร้าง GUID โดยใช้สองประเภทนี้เพื่อประกอบ GUID ด้วยการเรียกสี่ส่วน ดังนี้: XXXXXXXX -XXXX-XXXX -XXXX-XXXX XXXXXXXX.
  • เพื่อแยกความแตกต่างระหว่างสองประเภทนี้ ฉันได้เพิ่มพารามิเตอร์แฟลกให้กับฟังก์ชันสร้างคู่ _p8(s) พารามิเตอร์ s บอกฟังก์ชันว่าควรเพิ่มขีดกลางหรือไม่
  • ท้ายที่สุดเราสร้าง GUID ด้วยการเชื่อมต่อดังต่อไปนี้: _p8() + _p8(true) + _p8(true) + _p8() และส่งค่ากลับมัน

ลิงก์ไปยังโพสต์นี้บนบล็อกของฉัน

สนุกนะ! :-)

คำตอบ #8

นี่คือการใช้งานที่ไม่เป็นไปตามมาตรฐานเลยแต่มีประสิทธิภาพสูงมากในการสร้างตัวระบุที่ไม่ซ้ำกันเหมือน GUID ที่ปลอดภัยสำหรับ ASCII

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

สร้างตัวอักษร 26 ตัว [a-z0-9] ให้ UID ที่ทั้งสั้นกว่าและมีเอกลักษณ์มากกว่า GUID ที่เป็นไปตาม RFC สามารถเพิ่มขีดกลางได้อย่างง่ายดายหากความสามารถในการอ่านของมนุษย์สำคัญ

นี่คือตัวอย่างการใช้งานและเวลาที่ใช้สำหรับฟังก์ชันนี้และคำตอบอื่น ๆ ของคำถามนี้ การวัดเวลาได้ทำภายใต้ Chrome m25 จำนวน 10 ล้านรอบในแต่ละครั้ง

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

นี่คือรหัสเวลานัด

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

คำตอบ #9

จากบล็อกเทคนิคของ 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;
}

ยังมีวิธีอื่นที่เกี่ยวข้องกับการใช้คอนโทรล ActiveX แต่ควรหลีกเลี่ยงวิธีเหล่านี้!

ฉันคิดว่าควรชี้ให้เห็นว่าไม่มีเครื่องสร้าง GUID ใดสามารถรับประกันกุญแจที่ไม่ซ้ำกันได้ (ตรวจสอบบทความในวิกิพีเดีย) มักจะมีโอกาสเกิดการชนกันอยู่เสมอ GUID เพียงแค่เสนอจักรวาลของกุญแจที่มีขนาดใหญ่เพียงพอเพื่อลดโอกาสการชนกันให้เกือบเป็นศูนย์

คำตอบ #10

นี่คือตัวอย่างการรวมคำตอบที่ได้รับการโหวตมากที่สุด พร้อมวิธีแก้ปัญหาสำหรับการชนกันของ 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);
        });
    };

มันอยู่บน jsbin ถ้าคุณอยากทดสอบมัน

คำตอบ #11

นี่คือวิธีแก้ปัญหาที่มีวันที่ 9 ตุลาคม 2011 จากความคิดเห็นโดยผู้ใช้ jed ที่ 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)}

นี่ช่วยให้บรรลุเป้าหมายเดียวกับคำตอบที่ได้รับการให้คะแนนสูงสุดปัจจุบัน แต่ใช้ขนาดข้อมูลน้อยกว่า 50+ ไบต์โดยใช้ประโยชน์จากการบังคับชนิดข้อมูล การเรียกซ้ำ และสัญกรณ์แบบยกกำลัง สำหรับผู้ที่อยากรู้ว่ามันทำงานอย่างไร นี่คือตัวอย่างฟังก์ชันรุ่นเก่าที่มีคำอธิบายประกอบ:

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

คำตอบ #12

วิธีแก้ปัญหาแบบบรรทัดเดียวโดยใช้ Blobs

window.URL.createObjectURL(new Blob([])).substring(31);

ค่าที่อยู่ท้ายสุด (31) ขึ้นอยู่กับความยาวของ URL


แก้ไข:

วิธีแก้ปัญหาที่กระชับและเป็นสากลมากขึ้น ตามที่ rinogo แนะนำ:

URL.createObjectURL(new Blob([])).slice(-36);

คำตอบ #13

คุณสามารถใช้ node-uuid ได้ มันให้การสร้าง UUID ตาม RFC4122 ที่ง่ายและรวดเร็ว

คุณสมบัติ:

  • สร้าง UUID ตาม RFC4122 รุ่น 1 หรือ รุ่น 4
  • ทำงานใน Node.js และเว็บเบราว์เซอร์
  • การสร้างตัวเลขสุ่มที่มีความปลอดภัยทางคริปโตกราฟีบนแพลตฟอร์มที่สนับสนุน
  • รอยเท้าเล็ก (อยากได้อะไรที่เล็กกว่านี้ไหม? ลองดูสิ!)

ติดตั้งโดยใช้ NPM:

npm install uuid

หรือการใช้ uuid ผ่านเบราว์เซอร์:

ดาวน์โหลดไฟล์ดิบ (uuid v1): https://raw.githubusercontent.com/kelektiv/node-uuid/master/v1.js ดาวน์โหลดไฟล์ดิบ (uuid v4): https://raw.githubusercontent.com/kelektiv/node-uuid/master/v4.js


อยากได้เล็กกว่านี้อีกไหม? ลองดูอันนี้: https://gist.github.com/jed/982883


การใช้งาน:

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

ECMAScript 2015 (ES6):

import uuid from 'uuid/v4';
const id = uuid();

คำตอบ #14

นี่สร้าง UUID เวอร์ชัน 4 (สร้างจากตัวเลขสุ่มเทียม):

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

นี่คือตัวอย่างของ UUID ที่ถูกสร้างขึ้น:

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

คำตอบ #15

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

เวอร์ชันนี้อิงจากคำตอบของ Briguy37 และตัวดำเนินการบิตเล็กบางตัวเพื่อดึงหน้าต่างขนาดนิเบิลจากบัฟเฟอร์

มันควรปฏิบัติตามโครงสร้าง RFC Type 4 (สุ่ม) เนื่องจากผมมี ปัญหา ครั้งที่แล้วในการแยกวิเคราะห์ UUID ที่ไม่เป็นไปตามมาตรฐานด้วย UUID ของ Java

คำตอบ #16

โมดูล JavaScript ง่าย ๆ ที่เป็นการรวมของคำตอบที่ดีที่สุดในคำถามนี้

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);
    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('1. New Guid:   ' + Guid.newGuid());

// DEMO: Show empty GUID
console.log('2. Empty Guid: ' + Guid.empty);

การใช้งาน :

Guid.newGuid()

c6c2d12f-d76b-5739-e551-07e6de5b0807

Guid.empty

"00000000-0000-0000-0000-000000000000"

คำตอบ #17

เพิ่มใน: v15.6.0, v14.17.0 มีฟังก์ชัน crypto.randomUUID() ในตัว

import { randomUUID } from "node:crypto";

const uuid = crypto.randomUUID();

ในเบราว์เซอร์, crypto.randomUUID() ปัจจุบันรองรับใน Chromium 92+ และ Firefox 95+.

คำตอบ #18

เวอร์ชันด้านล่างเป็นการดัดแปลงจากคำตอบของ broofa แต่มีการอัปเดตให้รวมถึงฟังก์ชันสุ่ม "จริง" ที่ใช้ไลบรารีเข้ารหัสเมื่อมี และฟังก์ชัน Alea() เป็นตัวสำรอง

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

คำตอบ #19

โปรเจกต์ JavaScript บน GitHub - https://github.com/LiosK/UUID.js

UUID.js ตัวสร้าง UUID ที่เป็นไปตามมาตรฐาน RFC สำหรับ JavaScript

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

คุณสมบัติ สร้าง UUID ที่เป็นไปตามข้อกำหนด RFC 4122

UUID เวอร์ชัน 4 (UUID จากตัวเลขสุ่ม) และ UUID เวอร์ชัน 1 (UUID แบบอิงเวลา) มีให้ใช้งาน

วัตถุ UUID อนุญาตให้เข้าถึง UUID ได้หลายวิธี รวมถึงการเข้าถึงฟิลด์ของ UUID

ความละเอียดของเวลาตราประทับต่ำของ JavaScript ถูกชดเชยด้วยตัวเลขสุ่ม

คำตอบ #20

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

คำตอบ #21

สำหรับผู้ที่ต้องการโซลูชันที่สอดคล้องกับ RFC 4122 รุ่น 4 พร้อมการพิจารณาความเร็ว (เรียกใช้ 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() );

ฟังก์ชันข้างต้นควรมีสมดุลที่เหมาะสมระหว่างความเร็วและความสุ่ม

คำตอบ #22

ฉันไม่สามารถหาคำตอบใด ๆ ที่ใช้ TypedArray ขนาด 16 ไบต์เดียวและ DataView ได้ ดังนั้นฉันคิดว่าโซลูชันต่อไปนี้สำหรับการสร้าง UUID เวอร์ชัน 4 ตาม RFC จะยืนหยัดด้วยตัวเองที่นี่ได้:

const uuid4 = () => {
    const ho = (n, p) => n.toString(16).padStart(p, 0); /// Return the hexadecimal text representation of number `n`, padded with zeroes to be of length `p`; e.g. `ho(13, 2)` returns `"0d"`
    const data = crypto.getRandomValues(new Uint8Array(16)); /// Fill a buffer with random bits
    data[6] = (data[6] & 0xf) | 0x40; /// Patch the 6th byte to reflect a version 4 UUID
    data[8] = (data[8] & 0x3f) | 0x80; /// Patch the 8th byte to reflect a variant 1 UUID (version 4 UUIDs are)
    const view = new DataView(data.buffer); /// Create a view backed by the 16-byte buffer
    return `${ho(view.getUint32(0), 8)}-${ho(view.getUint16(4), 4)}-${ho(view.getUint16(6), 4)}-${ho(view.getUint16(8), 4)}-${ho(view.getUint32(10), 8)}${ho(view.getUint16(14), 4)}`; /// Compile the canonical textual form from the array data
};

ฉันชอบมันเพราะว่า:

  • มันพึ่งพาเฉพาะฟังก์ชันที่มีในแพลตฟอร์ม ECMAScript มาตรฐานเท่านั้น หากเป็นไปได้ -- ซึ่งเกือบทุกขั้นตอนยกเว้นหนึ่งขั้นตอน
  • มันใช้บัฟเฟอร์เพียงตัวเดียว ลดการคัดลอกข้อมูล ซึ่งในทางทฤษฎีควรจะให้ประสิทธิภาพที่ดีกว่า

ณ เวลาที่เขียนนี้ getRandomValues ยังไม่ได้ถูกนำไปใช้งานสำหรับอ็อบเจ็กต์ crypto ใน Node.js อย่างไรก็ตาม มันมีฟังก์ชันเทียบเท่า randomBytes ซึ่งสามารถใช้แทนได้

คำตอบ #23

ฉันต้องการเข้าใจคำตอบของ broofa ดังนั้นฉันจึงขยายมันและเพิ่มความคิดเห็น:

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

คำตอบ #24

ฟังก์ชันเนทีฟ URL.createObjectURL กำลังสร้าง UUID คุณสามารถใช้ประโยชน์จากสิ่งนี้ได้

function uuid() {
  const url = URL.createObjectURL(new Blob())
  const [id] = url.toString().split('/').reverse()
  URL.revokeObjectURL(url)
  return id
}

คำตอบ #25

ตัวอย่าง ES6

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

คำตอบ #26

ฉันปรับตัวสร้าง UUID/GUID ของตัวเองพร้อมกับฟีเจอร์เสริมบางอย่างที่นี่

ฉันกำลังใช้ตัวสร้างตัวเลขสุ่ม Kybos ต่อไปนี้เพื่อให้มีความปลอดภัยทางคริปโตกราฟีมากขึ้นเล็กน้อย

ด้านล่างนี้เป็นสคริปต์ของฉันโดยไม่รวมวิธี Mash และ Kybos จาก baagoe.com

//UUID/Guid Generator
// use: UUID.create() or UUID.createSequential()
// convenience:  UUID.empty, UUID.tryParse(string)
(function(w){
  // From http://baagoe.com/en/RandomMusings/javascript/
  // Johannes Baagøe <[email >, 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));

คำตอบ #27

วิธีที่ดีกว่า:

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,
                  ?              // generate 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
 }

ย่อเล็กสุด:

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}

คำตอบ #28

ต่อไปนี้เป็นโค้ดง่ายๆ ที่ใช้ crypto.getRandomValues(a) บนเบราว์เซอร์ที่รองรับ (Internet Explorer 11+, iOS 7+, Firefox 21+, Chrome และ Android Chrome)

มันหลีกเลี่ยงการใช้ Math.random() เพราะนั่นอาจทำให้เกิดการชนกัน (ตัวอย่างเช่น 20 การชนกันจาก UUID 4000 ตัวที่สร้างขึ้นในสถานการณ์จริงโดย 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);
}

บันทึก:

  • ปรับให้เหมาะสมกับการอ่านโค้ด ไม่ใช่ความเร็ว ดังนั้นจึงเหมาะสำหรับ เช่น ไม่กี่ร้อย UUID ต่อวินาที มันสร้าง uuid() ประมาณ 10000 ตัวต่อวินาทีใน Chromium บนแล็ปท็อปของฉันโดยใช้ http://jsbin.com/fuwigo/1 เพื่อวัดประสิทธิภาพ
  • มันใช้เพียง 8 สำหรับ "y" เพราะนั่นทำให้อ่านโค้ดง่ายขึ้น (y สามารถเป็น 8, 9, A หรือ B)

คำตอบ #29

ถ้าคุณแค่ต้องการสตริงแบบสุ่มขนาด 128 บิตโดยไม่ต้องมีรูปแบบเฉพาะ คุณสามารถใช้:

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

ซึ่งจะคืนค่าบางอย่างเช่น 2350143528-4164020887-938913176-2513998651

คำตอบ #30

แค่ตัวแปรอีกแบบที่อ่านง่ายกว่าโดยมีการกลายพันธุ์เพียงสองอย่าง

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

บทความที่เกี่ยวข้อง