为了我的 JavaScript 网站应用程序,我想生成短的 GUID(用于不同类型的对象 - 字符串和字符串数组)
我希望我的 UID(GUID)类似于 "aX4j9Z"。
所以这些 UID 应该足够轻量级,以便进行 Web 传输和 JS 字符串处理,并且对于结构不是非常庞大的情况下(不超过10k个元素),具有相当的唯一性。我的意思是,在生成 UID 后,我可以检查此 UID 是否已存在于结构中,并在需要时重新生成它。
为了我的 JavaScript 网站应用程序,我想生成短的 GUID(用于不同类型的对象 - 字符串和字符串数组)
我希望我的 UID(GUID)类似于 "aX4j9Z"。
所以这些 UID 应该足够轻量级,以便进行 Web 传输和 JS 字符串处理,并且对于结构不是非常庞大的情况下(不超过10k个元素),具有相当的唯一性。我的意思是,在生成 UID 后,我可以检查此 UID 是否已存在于结构中,并在需要时重新生成它。
如果您没有特殊需求,请查看@Mohamed的答案,使用预打包的解决方案(shortid
包)比其他任何解决方案更好。
使用6个字符的字母数字序列已足够随机索引10k个集合(366=22亿,363=46656)。
function generateUID() {
// I generate the UID from two parts here
// to ensure the random number provide enough bits.
var firstPart = (Math.random() * 46656) | 0;
var secondPart = (Math.random() * 46656) | 0;
firstPart = ("000" + firstPart.toString(36)).slice(-3);
secondPart = ("000" + secondPart.toString(36)).slice(-3);
return firstPart + secondPart;
}
随机生成的UID在生成约 √N 个数字后将会发生碰撞(生日悖论),因此为了安全起见,需要生成6位数而无需进行检查(旧版本仅生成4位数,如果不进行检查,则在生成1300个ID后就会发生碰撞)。
如果进行碰撞检查,则数字位数可以减少3或4位,但请注意,当您生成越来越多的UID时,性能将线性降低。
var _generatedUIDs = {};
function generateUIDWithCollisionChecking() {
while (true) {
var uid = ("0000" + ((Math.random() * Math.pow(36, 4)) | 0).toString(36)).slice(-4);
if (!_generatedUIDs.hasOwnProperty(uid)) {
_generatedUIDs[uid] = true;
return uid;
}
}
}
考虑使用顺序生成器(例如user134_item1
,user134_item2
,…),如果您需要唯一性而不是不可预测性。您可以"哈希"顺序生成的字符串以恢复不可预测性。Math.random
生成的UID不安全(您也不应该信任客户端)。在关键任务中不要依赖于其唯一性或不可预测性。shortid
已被nanoid取代,它更小更快:
- 小巧。 108字节(经过最小化处理和gzip压缩)。无依赖项。Size Limit控制大小。
- 快速。比UUID快40%。
- 安全。它使用密码学强大的随机API。可在集群中使用。
- 紧凑。它使用比UUID更大的字母表(A-Za-z0-9_-)。因此,ID大小从36减少到21个符号。
- 可移植。Nano ID已移植到14种编程语言。
import { nanoid } from 'nanoid'
// 21 characters (default)
// ~149 billion years needed, in order to have a 1% probability of at least one collision.
console.log(nanoid()) //=> "V1StGXR8_Z5jdHi6B-myT"
// 11 characters
// ~139 years needed, in order to have a 1% probability of at least one collision.
console.log(nanoid(11)) //=> "bdkjNOkq9PO"
了解更多信息:https://zelark.github.io/nano-id-cc/
还有一个很棒的npm包:shortid
惊人的短且不连续友好的URL唯一ID生成器。
ShortId创建惊人短且不连续的友好的URL唯一ID,非常适合用于URL缩短、MongoDB和Redis id以及用户可能会看到的任何其他id。
- 默认情况下是7-14个友好的URL字符:A-Z、a-z、0-9、_-
- 不连续,因此不可预测。
- 支持集群(自动)、自定义种子、自定义字母表。
- 可以生成任意数量的id而不重复,甚至每天数百万次。
- 非常适合游戏,特别是如果您担心作弊,因此不希望使用易于猜测的ID。
- 应用程序可以重新启动任意次数,而不会重复使用ID。
- 流行的Mongo ID/Mongoose ID替代品。
- 适用于Node、io.js和Web浏览器。
- 包括Mocha测试。
var shortid = require('shortid');
console.log(shortid.generate()); //PPBqWA9
replace(/[-]/g, '')
,这样可以缩短到32个字符长度。 - What Would Be Cool这里有一个一行代码,但它只会提供小写字母和数字:
var uuid = Math.random().toString(36).slice(-6);
console.log(uuid);
Date.now()
来获取具有一定含义的序列:
Math.floor(Date.now() / 1000).toString(36);
- Campbeln获取一个从100000000开始的简单计数器,将该数字转换为36进制。
(100000000).toString(36); //1njchs
(2100000000).toString(36); //yqaadc
就像 YouTube 一样,您可以轻松地拥有 20 亿个优雅独特的 ID。
(Math.round(Date.now())).toString(36)
- davefor (let i = 0; i < 1000; i++) { console.log(Math.round(Date.now()).toString(36)); }
直接在Chrome浏览器的控制台中输入,每次批处理约有50个重复的ID。如果一次处理大量数据,这将不必要地限制您的数据处理能力。 - StevenMath.round()
包装Date.now()
,因为Date.now()
会返回一个整数。否则,当ID只需要偶尔使用一次时,自然的解决方案就是这样。 - StevenCrypto.getRandomValues()
和 Uint8Array()
。
const hashID = size => {
const MASK = 0x3d
const LETTERS = 'abcdefghijklmnopqrstuvwxyz'
const NUMBERS = '1234567890'
const charset = `${NUMBERS}${LETTERS}${LETTERS.toUpperCase()}`.split('')
const bytes = new Uint8Array(size)
crypto.getRandomValues(bytes)
return bytes.reduce((acc, byte) => `${acc}${charset[byte & MASK]}`, '')
}
console.log({id: hashID(6)})
该实现使用以下字符:[A-Z
], [a-z
], [0-9
],总共有62个字符,如果我们添加 _
和 -
,则可以完整达到64个字符,如下所示:
const hashID = size => {
const MASK = 0x3d
const LETTERS = 'abcdefghijklmnopqrstuvwxyz'
const NUMBERS = '1234567890'
const charset = `${NUMBERS}${LETTERS}${LETTERS.toUpperCase()}_-`.split('')
const bytes = new Uint8Array(size)
crypto.getRandomValues(bytes)
return bytes.reduce((acc, byte) => `${acc}${charset[byte & MASK]}`, '')
}
console.log(`id: ${hashID(6)}`)
每小时生成1000个ID,每个ID长度为6个字符,需要大约2天的时间才能达到至少发生一次碰撞的1%概率。在将其应用于您的项目时,请牢记这一点。
crypto.randomUUID
方法:https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID - GrafluxerandomUUID
是一个生成包含36个字符长UUID的字符串的函数。其次,它需要使用HTTPS。因此,它太具体了,对于提出问题的用户可能没有用处。 - Teoccivar nextId = (function() {
var nextIndex = [0,0,0];
var chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
var num = chars.length;
return function() {
var a = nextIndex[0];
var b = nextIndex[1];
var c = nextIndex[2];
var id = chars[a] + chars[b] + chars[c];
a = ++a % num;
if (!a) {
b = ++b % num;
if (!b) {
c = ++c % num;
}
}
nextIndex = [a, b, c];
return id;
}
}());
var letters = 'abcdefghijklmnopqrstuvwxyz';
var numbers = '1234567890';
var charset = letters + letters.toUpperCase() + numbers;
function randomElement(array) {
with (Math)
return array[floor(random()*array.length)];
}
function randomString(length) {
var R = '';
for(var i=0; i<length; i++)
R += randomElement(charset);
return R;
}
with(Math)
这种让人想吐的写法 :) - user578895with
关键字可以在非性能代码中使用而无需担心问题,且 "with
is EVIL" 的说法很容易被夸大了。=)这里既不涉及性能问题(如果仅是性能问题,那就不要使用它),也不会创建或覆盖变量(因为不进行任何赋值操作),也不会与全局变量混淆。相较于重新定义整个 Math 模块的全局作用域,我更愿意承受一定的性能损失。 - ninjageckowith (Math)
并且定义了一个变量 var max = ...
那么就会覆盖掉 Math.max
......... 好的,不再使用 with
了。 - ninjageckofloor
和random
实际上是什么。 - user578895var IdGenerator = (function () {
var defaultCharset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890!@#$%^&*()_-+=[]{};:?/.>,<|".split("");
var IdGenerator = function IdGenerator(charset) {
this._charset = (typeof charset === "undefined") ? defaultCharset : charset;
this.reset();
};
IdGenerator.prototype._str = function () {
var str = "",
perm = this._perm,
chars = this._charset,
len = perm.length,
i;
for (i = 0; i < len; i++) {
str += chars[perm[i]];
}
return str;
};
IdGenerator.prototype._inc = function () {
var perm = this._perm,
max = this._charset.length - 1,
i;
for (i = 0; true; i++) {
if (i > perm.length - 1) {
perm.push(0);
return;
} else {
perm[i]++;
if (perm[i] > max) {
perm[i] = 0;
} else {
return;
}
}
}
};
IdGenerator.prototype.reset = function () {
this._perm = [];
};
IdGenerator.prototype.current = function () {
return this._str();
};
IdGenerator.prototype.next = function () {
this._inc();
return this._str();
};
return IdGenerator;
}).call(null);
使用方法:
var g = new IdGenerator(),
i;
for (i = 0; i < 100; i++) {
console.log(g.next());
}
这个代码片段包含上述实现和一个递归版本。
Math.random()
与计数器相结合。
Math.random()
应该提供约53位的熵(与UUIDv4的128位相比),但当与计数器结合时,应该可以提供足够的唯一性用于临时 ID。
let _id_counter = 0
function id() {
return '_' + (_id_counter++).toString(36) + '_' + Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(36)
}
console.log(Array.from({length: 100}).map(() => id()))
特点:
id
和React的key
你可以将GUID缩短为20个可打印的ASCII字符,而不会丢失信息或GUID的唯一性。
Jeff Atwood多年前就在博客中谈到了这个问题:
Equipping our ASCII Armor