Câu hỏi Tạo GUID / UUID trong JavaScript?


Tôi đang cố gắng tạo mã định danh duy nhất trên toàn cầu bằng JavaScript. Tôi không chắc chắn những thói quen nào có sẵn trên tất cả các trình duyệt, cách "ngẫu nhiên" và tạo hạt giống trình tạo số ngẫu nhiên tích hợp, v.v.

GUID / UUID phải có ít nhất 32 ký tự và phải nằm trong phạm vi ASCII để tránh sự cố khi truyền chúng xung quanh.


3219


gốc


GUID khi được lặp lại dưới dạng chuỗi có ít nhất 36 và không quá 38 ký tự và khớp với mẫu ^ \ {? [A-zA-Z0-9] {36}? \} $ Và do đó luôn là ascii. - AnthonyWJones
David Bau cung cấp một trình tạo số ngẫu nhiên tốt hơn, có thể gieo hạt tại davidbau.com/archives/2010/01/30/… Tôi đã viết một cách tiếp cận hơi khác để tạo UUID tại blogs.cozi.com/tech/2010/04/generating-uuids-in-javascript.html - George V. Reilly
jsben.ch/#/Lbxoe - đây là điểm chuẩn với các chức năng khác nhau từ bên dưới - EscapeNetscape
uuid-random sử dụng PRNG tốt và rất nhanh. - jchook
Muộn (rất muộn!) Cho bữa tiệc ở đây nhưng @AnthonyWJones không nên đọc regex của bạn: ^ \ {? [A-fA-F0-9] {36}? \} $ - noonand


Các câu trả lời:


Đã có một vài nỗ lực này. Câu hỏi đặt ra là: bạn có muốn GUID thực sự hay chỉ các số ngẫu nhiên nhìn như GUID? Thật dễ dàng để tạo ra các số ngẫu nhiên.

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

Tuy nhiên, lưu ý rằng các giá trị như vậy không phải là GUID chính hãng.

Không có cách nào để tạo các GUID thực trong Javascript, bởi vì chúng phụ thuộc vào các thuộc tính của máy tính cục bộ mà các trình duyệt không trưng ra. Bạn sẽ cần sử dụng các dịch vụ dành riêng cho hệ điều hành như ActiveX: http://p2p.wrox.com/topicindex/20339.htm

Chỉnh sửa: không chính xác - RFC4122 cho phép GUIDs ("phiên bản 4") ngẫu nhiên. Xem các câu trả lời khác để biết chi tiết cụ thể.

chú thích: đoạn mã được cung cấp không tuân theo RFC4122 yêu cầu phiên bản (4) phải được tích hợp vào chuỗi đầu ra được tạo. Không sử dụng câu trả lời này nếu bạn cần GUID tương thích.

Sử dụng:

var uuid = guid();

Bản giới thiệu:

function guid() {
  return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
    s4() + '-' + s4() + s4() + s4();
}

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

document.getElementById('jsGenId').addEventListener('click', function() {
  document.getElementById('jsIdResult').value = guid();
})
input { font-family: monospace; }
<button id="jsGenId" type="button">Generate GUID</button>
<br>
<input id="jsIdResult" type="text" placeholder="Results will be placed here..." readonly size="40"/>


1904



Trên thực tế, RFC cho phép UUID được tạo từ các số ngẫu nhiên. Bạn chỉ cần phải twiddle một vài bit để xác định nó như vậy. Xem phần 4.4. Các thuật toán để tạo UUID từ các số ngẫu nhiên hoặc giả ngẫu nhiên: rfc-archive.org/getrfc.php?rfc=4122 - Jason DeFontes
Ai đó có thể giải thích mã này cho tôi? Có vẻ như hàm S4 cố gắng lấy một số hex ngẫu nhiên giữa 0x10000 và 0x20000, sau đó xuất ra 4 chữ số cuối cùng. Nhưng tại sao bitwise hoặc bằng 0? Đó không phải là một noop sao? Ngoài ra, là 0x10000 đến 0x20000 chỉ là một hack để tránh phải đối phó với hàng đầu của 0? - Cory
Trong Chrome, mã này không phải lúc nào cũng tạo GUID kích thước chính xác. Độ dài thay đổi từ 35 đến 36 - cdeutsch
Làm thế nào có thể một câu trả lời rõ ràng như vậy sai nhận được rất nhiều upvotes? Ngay cả mã là sai, vì không có 4 ở đúng vị trí. en.wikipedia.org/wiki/Globally_unique_identifier - Dennis Krøger
Câu trả lời này đã bị hỏng trong bản sửa đổi 5 khi "1 + ...." và "chuỗi con (1)" đã bị xóa. Những bit đó đảm bảo độ dài nhất quán. - Segfault


Cho một RFC4122 giải pháp tương thích phiên bản 4, giải pháp một lớp (ish) này là nhỏ gọn nhất mà tôi có thể đưa ra .:

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

Cập nhật, 2015-06-02: Hãy lưu ý rằng tính độc đáo UUID phụ thuộc rất nhiều vào bộ tạo số ngẫu nhiên cơ bản (RNG). Giải pháp trên sử dụng Math.random() cho ngắn gọn, tuy nhiên Math.random() Là không phải đảm bảo trở thành một RNG chất lượng cao. Xem Adam Hyland's viết tuyệt vời trên Math.random () để biết chi tiết. Để có giải pháp mạnh mẽ hơn, hãy cân nhắc điều gì đó như mô-đun uuid[Disclaimer: Tôi là tác giả], sử dụng các API RNG chất lượng cao hơn nếu có.

Cập nhật, 2015-08-26: Là một lưu ý phụ, điều này ý chính mô tả cách xác định số lượng ID có thể được tạo trước khi đạt được xác suất va chạm nhất định. Ví dụ: với 3,26x1015 phiên bản 4 UFC RFC4122 bạn có cơ hội va chạm 1-trong-một triệu.

Cập nhật, 2017-06-28: A bài viết hay từ các nhà phát triển Chrome thảo luận về trạng thái của chất lượng PR. Mathom PROSS trong Chrome, Firefox và Safari. tl; dr - Tính đến cuối năm 2015 nó "khá tốt", nhưng không phải là chất lượng mã hóa. Để giải quyết vấn đề đó, đây là phiên bản cập nhật của giải pháp trên sử dụng ES6, crypto API và một chút thuật sĩ JS tôi không thể lấy tín dụng cho:

function uuidv4() {
  return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
    (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
  )
}

console.log(uuidv4());


3150



Có an toàn khi sử dụng mã này để tạo các id duy nhất trên máy khách và sau đó sử dụng các id đó làm khóa chính để lưu các đối tượng trên máy chủ không? - Muxa
... (tiếp theo) Tỷ lệ cược của hai ID được tạo ra bởi hàm va chạm này, theo nghĩa đen, nhỏ về mặt thiên văn. Tất cả, nhưng 6 trong số 128 bit của ID được tạo ngẫu nhiên, có nghĩa là đối với bất kỳ hai id, có một 1 trong 2 ^^ 122 (hoặc 5.3x10 ^^ 36) cơ hội chúng sẽ va chạm. - broofa
Tôi đã đăng câu hỏi về xung đột stackoverflow.com/questions/6906916/… - Muxa
Chắc chắn câu trả lời cho câu hỏi của @ Muxa là 'không'? Nó không bao giờ thực sự an toàn để tin tưởng một cái gì đó đến từ khách hàng. Tôi đoán nó phụ thuộc vào khả năng người dùng của bạn sẽ mang đến một bảng điều khiển javascript và thay đổi biến theo cách thủ công thành thứ họ muốn. Hoặc họ chỉ có thể POST bạn trở lại id mà họ muốn. Nó cũng sẽ phụ thuộc vào việc người dùng chọn ID riêng của họ sẽ gây ra lỗ hổng. Dù bằng cách nào, nếu đó là một số ID ngẫu nhiên đang đi vào một bảng, tôi có thể sẽ tạo ra nó phía máy chủ, để tôi biết tôi có quyền kiểm soát quá trình. - Cam Jackson
@DrewNoakes - UUID không chỉ là một chuỗi các số ngẫu nhiên hoàn toàn. "4" là phiên bản uuid (4 = "ngẫu nhiên"). Dấu "y" trong đó biến thể uuid (bố cục trường, về cơ bản) cần được nhúng. Xem phần 4.1.1 và 4.1.3 của ietf.org/rfc/rfc4122.txt để biết thêm thông tin. - broofa


Tôi thực sự thích cách làm sạch Câu trả lời của Broofa là, nhưng thật không may là việc triển khai kém Math.random để lại cơ hội va chạm.

Đây là một điểm tương tự RFC4122 giải pháp tương thích phiên bản 4 giải quyết vấn đề đó bằng cách bù đắp 13 số hex đầu tiên bằng phần hex của dấu thời gian. Bằng cách đó, ngay cả khi Math.randomlà trên cùng một hạt giống, cả hai khách hàng sẽ phải tạo UUID cùng một mili giây chính xác (hoặc 10.000+ năm sau) để có cùng UUID:

function generateUUID() { // Public Domain/MIT
    var d = new Date().getTime();
    if (typeof performance !== 'undefined' && typeof performance.now === 'function'){
        d += performance.now(); //use high-precision timer if available
    }
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
        var r = (d + Math.random() * 16) % 16 | 0;
        d = Math.floor(d / 16);
        return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
    });
}


Đây là một fiddle để kiểm tra.


672



Ghi nhớ, new Date().getTime() không được cập nhật mỗi mili giây. Tôi không chắc làm thế nào điều này ảnh hưởng đến sự ngẫu nhiên kỳ vọng của thuật toán của bạn. - devios1
Tôi nghĩ rằng đây là câu trả lời tốt nhất đơn giản chỉ vì nó sử dụng ngày trong thế hệ của nó. Tuy nhiên, nếu bạn có một ngăn xếp trình duyệt hiện đại, tôi khuyên bạn nên Date.now() đến new Date().getTime() - Fresheyeball
performance.now sẽ còn tốt hơn nữa. Không giống như Date.now, dấu thời gian được trả về bởi performance.now() không giới hạn ở độ phân giải một phần nghìn giây. Thay vào đó, chúng biểu thị thời gian dưới dạng số dấu phẩy động với tối đa microsecond độ chính xác. Cũng không giống như Date.now, các giá trị được trả về bởi performance.now () luôn tăng với tốc độ không đổi, độc lập với đồng hồ hệ thống có thể được điều chỉnh thủ công hoặc bị lệch bởi phần mềm chẳng hạn như Giao thức thời gian mạng. - daniellmb
@ daniellmb Có lẽ bạn nên liên kết với MDN hoặc một tài khoản khác để hiển thị tài liệu thực và không phải là một polyfill;) - Martin
FYI, mỗi chân trang, tất cả đóng góp của người dùng trên trang web đều có sẵn theo giấy phép cc by-sa 3.0. - Xiong Chiamiov


Câu trả lời của broofa là khá trơn tru, thực sự - ấn tượng thông minh, thực sự ... tuân thủ rfc4122, phần nào có thể đọc được và nhỏ gọn. Tuyệt vời!

Nhưng nếu bạn nhìn vào biểu thức chính quy đó, replace() callbacks, toString()'cát Math.random() chức năng cuộc gọi (nơi anh ta chỉ sử dụng 4 bit của kết quả và lãng phí phần còn lại), bạn có thể bắt đầu tự hỏi về hiệu suất. Thật vậy, joelpt thậm chí đã quyết định tung ra RFC cho tốc độ GUID chung với generateQuickGUID.

Nhưng, chúng ta có thể có tốc độ không  RFC tuân thủ? Tôi nói "có!  Chúng ta có thể duy trì khả năng đọc được không? Ừm ... Không hẳn, nhưng thật dễ dàng nếu bạn theo dõi.

Nhưng trước tiên, kết quả của tôi, so với broofa, guid (câu trả lời được chấp nhận) và tuân thủ phi 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.

Vì vậy, bằng cách lặp lại lần thứ 6 của tôi về tối ưu hóa, tôi đã đánh bại câu trả lời phổ biến nhất qua 12X, câu trả lời được chấp nhận bởi hơn 9Xvà câu trả lời không tuân thủ nhanh 2-3X. Và tôi vẫn tuân thủ rfc4122.

Quan tâm đến cách thức? Tôi đã đặt toàn bộ nguồn trên http://jsfiddle.net/jcward/7hyaC/3/ và hơn thế nữa http://jsperf.com/uuid-generator-opt/4

Để giải thích, hãy bắt đầu với mã của broofa:

'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
  var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
  return v.toString(16);
});

Vì vậy, nó thay thế x với bất kỳ chữ số hex ngẫu nhiên nào, y với dữ liệu ngẫu nhiên (ngoại trừ việc buộc 2 bit hàng đầu vào 10 theo thông số RFC) và regex không khớp với - hoặc là 4 nhân vật, vì vậy anh ta không phải đối phó với họ. Rất, rất trơn.

Điều đầu tiên cần biết là các cuộc gọi hàm rất đắt, như là các biểu thức thông thường (mặc dù anh ta chỉ sử dụng 1, nó có 32 callbacks, một cho mỗi trận đấu, và trong mỗi 32 callbacks nó gọi Math.random () và v. toString (16)).

Bước đầu tiên hướng tới hiệu năng là loại bỏ RegEx và các chức năng gọi lại của nó và sử dụng một vòng lặp đơn giản thay thế. Điều này có nghĩa là chúng ta phải đối phó với - và 4 nhân vật trong khi broofa thì không. Ngoài ra, lưu ý rằng chúng ta có thể sử dụng String Array indexing để giữ cấu trúc template String slick của mình:

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

Về cơ bản, cùng một logic bên trong, ngoại trừ chúng tôi kiểm tra - hoặc là 4và sử dụng vòng lặp while (thay vì replace() callbacks) giúp chúng tôi cải tiến gần như 3 lần!

Bước tiếp theo là một bước nhỏ trên máy tính để bàn nhưng tạo sự khác biệt đáng kể trên thiết bị di động. Hãy làm cho ít Math.random () gọi và sử dụng tất cả những bit ngẫu nhiên thay vì ném 87% của chúng đi với một bộ đệm ngẫu nhiên được chuyển ra mỗi lần lặp. Chúng ta hãy di chuyển định nghĩa mẫu đó ra khỏi vòng lặp, chỉ trong trường hợp nó giúp:

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
}

Điều này tiết kiệm cho chúng tôi 10-30% tùy thuộc vào nền tảng. Không tệ. Nhưng bước lớn tiếp theo sẽ loại bỏ hàm toString hoàn toàn bằng một cổ điển tối ưu hóa - bảng tra cứu. Bảng tra cứu 16 phần tử đơn giản sẽ thực hiện công việc của toString (16) trong thời gian ít hơn nhiều:

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
}

Tối ưu hóa tiếp theo là một cổ điển khác. Vì chúng tôi chỉ xử lý 4 bit đầu ra trong mỗi vòng lặp lặp lại, hãy cắt số vòng lặp thành một nửa và xử lý 8 bit mỗi lần lặp. Điều này là khó khăn vì chúng ta vẫn phải xử lý các vị trí bit RFC tương thích, nhưng nó không quá khó. Sau đó chúng ta phải tạo một bảng tra cứu lớn hơn (16x16 hoặc 256) để lưu trữ 0x00 - 0xff và chúng ta chỉ xây dựng nó một lần, bên ngoài hàm 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
}

Tôi đã thử một e6 () xử lý 16 bit tại một thời điểm, vẫn sử dụng LUT 256 phần tử, và nó cho thấy lợi nhuận tối ưu hóa giảm dần. Mặc dù nó có ít lần lặp lại hơn, nhưng logic bên trong phức tạp hơn do việc xử lý tăng lên, và nó hoạt động tương tự trên máy tính để bàn và chỉ nhanh hơn ~ 10% trên thiết bị di động.

Kỹ thuật tối ưu hóa cuối cùng để áp dụng - bỏ vòng lặp. Vì chúng ta đang lặp lại một số lần cố định, chúng ta có thể viết tất cả bằng tay một cách kỹ thuật. Tôi đã thử điều này một lần với một biến ngẫu nhiên r duy nhất mà tôi giữ lại giao, và hiệu suất tanked. Nhưng với bốn biến được gán dữ liệu ngẫu nhiên lên phía trước, sau đó sử dụng bảng tra cứu và áp dụng các bit RFC thích hợp, phiên bản này hút tất cả chúng:

var lut = []; for (var i=0; i<256; i++) { lut[i] = (i<16?'0':'')+(i).toString(16); }
function e7()
{
  var d0 = Math.random()*0xffffffff|0;
  var d1 = Math.random()*0xffffffff|0;
  var d2 = Math.random()*0xffffffff|0;
  var d3 = Math.random()*0xffffffff|0;
  return lut[d0&0xff]+lut[d0>>8&0xff]+lut[d0>>16&0xff]+lut[d0>>24&0xff]+'-'+
    lut[d1&0xff]+lut[d1>>8&0xff]+'-'+lut[d1>>16&0x0f|0x40]+lut[d1>>24&0xff]+'-'+
    lut[d2&0x3f|0x80]+lut[d2>>8&0xff]+'-'+lut[d2>>16&0xff]+lut[d2>>24&0xff]+
    lut[d3&0xff]+lut[d3>>8&0xff]+lut[d3>>16&0xff]+lut[d3>>24&0xff];
}

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

Điều thú vị là, tạo ra 16 byte dữ liệu ngẫu nhiên là phần dễ dàng. Toàn bộ lừa được thể hiện bằng định dạng String với sự tuân thủ RFC, và nó được thực hiện chặt chẽ nhất với 16 byte dữ liệu ngẫu nhiên, một vòng lặp và bảng tra cứu chưa được kiểm tra.

Tôi hy vọng logic của tôi là chính xác - rất dễ mắc lỗi trong loại công việc tẻ nhạt này. Nhưng kết quả đầu ra có vẻ tốt với tôi. Tôi hy vọng bạn thích chuyến đi điên cuồng này thông qua tối ưu hóa mã!

Được khuyên: mục tiêu chính của tôi là hiển thị và dạy các chiến lược tối ưu hóa tiềm năng. Các câu trả lời khác bao gồm các chủ đề quan trọng như va chạm và các số ngẫu nhiên thực sự, điều quan trọng để tạo ra UUID tốt.


308



jsperf.com sẽ cho phép bạn nắm bắt dữ liệu và xem kết quả trên các trình duyệt và thiết bị. - fearphage
Xin chào @chad, các câu hỏi hay. k có thể được di chuyển bên ngoài, nhưng điều đó không cải thiện hiệu suất ở trên và làm cho phạm vi lộn xộn hơn. Và xây dựng một mảng và tham gia vào trở lại kỳ lạ giết chết hiệu suất. Nhưng một lần nữa, cảm thấy tự do để thử nghiệm! - Jeff Ward
Tôi rất tò mò muốn biết cách so sánh node-uuid.js. Trong công việc của tôi ở đó tôi bắt đầu với nhiều tập trung vào hiệu suất, một số trong đó vẫn còn. Nhưng tôi đã từ bỏ việc đó, thích có mã dễ đọc hơn / dễ bảo trì hơn. Lý do là uuid perf chỉ đơn giản là không phải là một vấn đề trong thế giới thực. Các Uuids thường được tạo ra kết hợp với các hoạt động chậm hơn nhiều (ví dụ: tạo một yêu cầu mạng, tạo và duy trì một đối tượng mô hình), trong đó cạo một vài micro giây ra khỏi mọi thứ đơn giản là không quan trọng. - broofa
Mã này vẫn còn chứa một vài lỗi: Math.random()*0xFFFFFFFF dòng nên được Math.random()*0x100000000 cho đầy đủ ngẫu nhiên, và >>>0 nên được sử dụng thay vì |0 để giữ cho các giá trị chưa được ký (mặc dù với mã hiện tại, tôi nghĩ nó sẽ bị xóa ngay cả khi chúng được ký). Cuối cùng nó sẽ là một ý tưởng rất tốt những ngày này để sử dụng window.crypto.getRandomValues nếu có, và quay trở lại Math.random chỉ khi thật cần thiết. Math.random có ​​thể có ít hơn 128 bit entropy, trong trường hợp này sẽ dễ bị tổn thương hơn khi va chạm hơn mức cần thiết. - Dave
Đã áp dụng tất cả các lời khuyên của @Dave và xuất bản rất gọn gàng nguồn ES6 / Babel ở đây: codepen.io/avesus/pen/wgQmaV?editors=0012 - Brian Haak


Dưới đây là một số mã dựa trên RFC 4122, phần 4.4 (Thuật toán để tạo UUID từ số ngẫu nhiên hoặc số ngẫu nhiên giả).

function createUUID() {
    // http://www.ietf.org/rfc/rfc4122.txt
    var s = [];
    var hexDigits = "0123456789abcdef";
    for (var i = 0; i < 36; i++) {
        s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
    }
    s[14] = "4";  // bits 12-15 of the time_hi_and_version field to 0010
    s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);  // bits 6-7 of the clock_seq_hi_and_reserved to 01
    s[8] = s[13] = s[18] = s[23] = "-";

    var uuid = s.join("");
    return uuid;
}

138



Điều này không tạo ra dấu gạch ngang cần thiết cho c # để phân tích cú pháp nó thành System.Guid. Nó ám như thế này: B42A153F1D9A4F92990392C11DD684D2, khi nó sẽ hiển thị như sau: B42A153F-1D9A-4F92-9903-92C11DD684D2 - Levitikon
ABNF từ spec bao gồm các ký tự "-", vì vậy tôi đã cập nhật để tuân thủ. - Kevin Hakanson
Cá nhân tôi ghét những dấu gạch ngang, nhưng cho riêng mình. Hey, đó là lý do chúng tôi lập trình! - devios1
Bạn nên khai báo kích thước mảng trước thay vì kích thước nó động khi bạn xây dựng GUID. var s = new Array(36); - MgSam
@Levitikon .NET's Guid.Parse() nên phân tích cú pháp B42A153F1D9A4F92990392C11DD684D2vào một Guid tốt. Nó không cần phải có dấu gạch nối. - JLRishe


GUID nhanh nhất như phương thức trình tạo chuỗi ở định dạng XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX. Điều này không tạo GUID tuân thủ tiêu chuẩn.

Mười triệu thực thi việc thực hiện này chỉ mất 32,5 giây, đó là tốc độ nhanh nhất mà tôi từng thấy trong một trình duyệt (giải pháp duy nhất không có vòng lặp / lặp).

Hàm này đơn giản như:

/**
 * Generates a GUID string.
 * @returns {String} The generated GUID.
 * @example af8a8416-6e18-a307-bd9c-f2c947bbb3aa
 * @author Slavik Meltser (slavik@meltser.info).
 * @link http://slavik.meltser.info/?p=142
 */
function guid() {
    function _p8(s) {
        var p = (Math.random().toString(16)+"000000000").substr(2,8);
        return s ? "-" + p.substr(0,4) + "-" + p.substr(4,4) : p ;
    }
    return _p8() + _p8(true) + _p8(true) + _p8();
}

Để kiểm tra hiệu suất, bạn có thể chạy mã này:

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

Tôi chắc rằng hầu hết các bạn sẽ hiểu những gì tôi đã làm ở đó, nhưng có lẽ có ít nhất một người sẽ cần một lời giải thích:

Thuật toán:

  • Các Math.random() Hàm trả về một số thập phân giữa 0 và 1 với 16 chữ số sau dấu thập phân (cho thí dụ 0.4363923368509859).
  • Sau đó, chúng tôi lấy số này và chuyển đổi nó đến một chuỗi với cơ số 16 (từ ví dụ trên chúng ta sẽ nhận được 0.6fb7687f).
    Math.random().toString(16).
  • Sau đó, chúng tôi cắt 0. tiếp đầu ngữ (0.6fb7687f => 6fb7687f) và nhận được một chuỗi với tám thập lục phân kí tự dài.
    (Math.random().toString(16).substr(2,8).
  • Đôi khi Math.random()chức năng sẽ trở lại số ngắn hơn (ví dụ 0.4363), do số không ở cuối (từ ví dụ trên, thực ra con số này là 0.4363000000000000). Đó là lý do tại sao tôi phụ thêm vào chuỗi này "000000000" (một chuỗi với chín số không) và sau đó cắt nó với substr() chức năng để làm cho nó chín ký tự chính xác (điền số 0 ở bên phải).
  • Lý do để thêm chính xác chín số không là vì kịch bản trường hợp xấu hơn, đó là khi Math.random() Hàm sẽ trả về chính xác 0 hoặc 1 (xác suất là 1/10 ^ 16 cho mỗi một trong số chúng). Đó là lý do tại sao chúng tôi cần thêm chín số không vào nó ("0"+"000000000" hoặc là "1"+"000000000") và sau đó cắt nó ra khỏi chỉ mục thứ hai (ký tự thứ 3) với độ dài tám ký tự. Đối với phần còn lại của các trường hợp, việc bổ sung các số không sẽ không gây hại cho kết quả vì nó đang cắt nó đi.
    Math.random().toString(16)+"000000000").substr(2,8).

Hội đồng:

  • GUID có định dạng sau XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.
  • Tôi chia GUID thành 4 phần, mỗi phần được chia thành 2 loại (hoặc định dạng): XXXXXXXX và -XXXX-XXXX.
  • Bây giờ tôi đang xây dựng GUID bằng cách sử dụng 2 loại này để lắp ráp GUID với cuộc gọi 4 miếng, như sau: XXXXXXXX  -XXXX-XXXX  -XXXX-XXXX  XXXXXXXX.
  • Để khác biệt giữa hai loại này, tôi đã thêm thông số cờ vào chức năng tạo cặp _p8(s), các s tham số cho biết chức năng có thêm dấu gạch ngang hay không.
  • Cuối cùng, chúng tôi xây dựng GUID với chuỗi sau: _p8() + _p8(true) + _p8(true) + _p8()và trả lại.

Liên kết tới bài đăng này trên blog của tôi

Thưởng thức! :-)


79



Triển khai này không chính xác. Một số ký tự của GUID yêu cầu xử lý đặc biệt (ví dụ: chữ số 13 cần phải là số 4). - JLRishe
@ JLRishe, bạn nói đúng, nó không tuân theo các tiêu chuẩn RFC4122. Nhưng nó vẫn là một chuỗi ngẫu nhiên trông giống như GUID. Chúc mừng :-) - Slavik Meltser
Công việc tuyệt vời, nhưng kỹ thuật tối ưu hóa cổ điển làm cho nó nhanh hơn 6 lần (trên trình duyệt của tôi) - xem câu trả lời của tôi - Jeff Ward


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

Nếu ID được tạo cách nhau hơn 1 mili giây, chúng là 100% duy nhất.

Nếu hai ID được tạo ra trong khoảng thời gian ngắn hơn và giả sử rằng phương thức ngẫu nhiên thực sự là ngẫu nhiên, điều này sẽ tạo ra ID là 99.99999999999999% có khả năng là duy nhất toàn cầu (va chạm trong 1 trong số 10 ^ 15)

Bạn có thể tăng số này bằng cách thêm nhiều chữ số hơn, nhưng để tạo ra 100% ID duy nhất, bạn sẽ cần sử dụng bộ đếm chung.

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


69



Đây không phải là UUID? - Marco Kerwitz
Số UUID / GUID là số 122 bit (+ sáu bit được đặt trước). nó có thể đảm bảo tính duy nhất thông qua một dịch vụ truy cập toàn cầu, nhưng thường nó chuyển tiếp theo thời gian, địa chỉ MAC và ngẫu nhiên. UUID không phải ngẫu nhiên! UID tôi đề xuất ở đây không được nén hoàn toàn. Bạn có thể nén nó, thành số nguyên 122 bit, thêm 6 bit được xác định trước và các bit ngẫu nhiên bổ sung (loại bỏ một vài bit hẹn giờ) và bạn kết thúc với UUID / GUID hoàn chỉnh, sau đó bạn phải chuyển đổi sang hex. Với tôi, điều đó không thực sự thêm bất cứ điều gì khác ngoài việc tuân thủ độ dài của ID. - Simon Rigét
Việc chuyển tiếp địa chỉ MAC cho tính duy nhất trên các máy ảo là một ý tưởng tồi! - Simon Rigét


Đây là sự kết hợp của câu trả lời bình chọn hàng đầu, với một giải pháp cho Xung đột của 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);
        });
    };

Trên jsbin nếu bạn muốn kiểm tra nó.


57



Tôi tin rằng trong IE nó thực sự là window.msCrypto thay vì window.crypto. Có thể là tốt đẹp để kiểm tra cả hai. Xem msdn.microsoft.com/en-us/library/ie/dn265046(v=vs.85).aspx - herbrandson
lưu ý rằng phiên bản đầu tiên, một phiên bản `window.crypto.getRandomValues, does not keep the Version 4 UUIDs format defined by RFC 4122. That is instead of xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx` sản lượng xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. - humanityANDpeace


Đây là giải pháp ngày 9 tháng 10 năm 2011 từ nhận xét của người dùng jed tại 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)}

Điều này hoàn thành mục tiêu giống như câu trả lời được đánh giá cao nhất hiện tại, nhưng trong ít hơn 50 byte bằng cách khai thác sự ép buộc, đệ quy và ký hiệu số mũ. Đối với những người tò mò làm thế nào nó hoạt động, đây là hình thức chú thích của một phiên bản cũ của hàm:

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



Trong TypeScript sử dụng điều này: export const UUID = function b (a: number): string { return a ? (a^Math.random()*16>>a/4).toString(16) : (''+[1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, b) }; - RyanNerd


Đây là một triển khai hoàn toàn không tuân thủ nhưng rất thực hiện để tạo ra một định danh duy nhất GUID giống như ASCII.

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

Tạo ra 26 ký tự [a-z0-9], sinh ra một UID ngắn hơn và độc đáo hơn so với GUID tương thích RFC. Dấu gạch ngang có thể được thêm trivially nếu vấn đề dễ đọc của con người.

Dưới đây là ví dụ sử dụng và thời gian cho chức năng này và một số câu trả lời khác của câu hỏi này. Thời gian được thực hiện dưới Chrome m25, mỗi lần có 10 triệu lần lặp lại.

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

Đây là mã thời gian.

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

52