• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

UUID生成策略/算法JavaScript / Typescript

武飞扬头像
星辰大海-H
帮助1

UUID生成策略/算法(JavaScript / Typescript)

  • 我只是想简单的生成一些UUID,不想引入一堆库,所以最后设计了一个简单的UUID生成策略。

  • 下面的UUID生成方法是依据时间戳随机数count计数器三个输入因子生成的,能保证本地的UUID是不会重复的,但是在多用户端环境下有非常非常低的概率出现重复。

  • 多用户端场景UUID有可能重复的极端场景,需要满足以下两个前提条件:

    • 两个用户端在同一毫秒内完成了此代码的初始化(获得了相同的时间戳)。
    • 两个用户生成的随机数相同(理论上相同的概率是一百亿分之一)。

实现代码(JavaScript / Typescript):

// UUID中允许出现的基本字符;
const baseChar: string[] = (() => {
  const array: string[] = [];
  // 将数字0-9加入到 baseChar 中
  for (let i = 0; i < 10; i  ) {
    array.push(i.toString(10));
  }
  // 将小写字母a-z加入到 baseChar 中
  for (let i = 0; i < 26; i  ) {
    array.push(String.fromCharCode('a'.charCodeAt(0)   i));
  }
  // 将大写字母A-Z加入到 baseChar 中
  for (let i = 0; i < 26; i  ) {
    array.push(String.fromCharCode('A'.charCodeAt(0)   i));
  }
  return array;
})();

// 进制转换/数字转字符
function scaleTransition(value: number, base = baseChar): string {
  if (value < 0) {
    throw new Error('scaleTransition Error, value < 0');
  }
  if (value === 0) {
    return base[0];
  }
  const radix = base.length;
  let result = '';
  let resValue = value;
  while (resValue > 0) {
    result = base[resValue % radix]   result;
    resValue = Math.floor(resValue / radix);
  }
  return result;
}

// UUID参数集合, 使用 count 能保证在本地环境中UUID绝对不会重复.
const UuidObject: Record<string, { prefix: string; count: number }> = {};
// 生成随机的UUID
export default function createUUID(prefix = '') {
  let uuidObject = UuidObject[prefix];
  if (uuidObject === undefined) {
    const str = `${scaleTransition(Date.now())}-${scaleTransition(
      Math.floor(Math.random() * 10000000000),
    )}-`;
    if (prefix) {
      uuidObject = {
        prefix: `${prefix}-${str}`,
        count: 0,
      };
    } else {
      uuidObject = {
        prefix: str,
        count: 0,
      };
    }
    UuidObject[prefix] = uuidObject;
  }
  return uuidObject.prefix   scaleTransition(uuidObject.count  );
}

学新通
  • 这里的baseChar中包含的是UUID中允许出现的字符。
  • scaleTransition会将传入的value进行“进制转换”。
  • 总得来说,baseChar其中包含的字符越多,最后生成的UUID长度越短。

进阶版,加入服务端前缀与MD5散列

import MD5 from 'md5';

// UUID中的分隔符
const splitChar = '.';
// UUID中允许出现的基本字符;
const baseChar: string[] = (() => {
  const array: string[] = [];
  // 将数字0-9加入到 baseChar 中
  for (let i = 0; i < 10; i  ) {
    array.push(i.toString(10));
  }
  // 将小写字母a-z加入到 baseChar 中
  for (let i = 0; i < 26; i  ) {
    array.push(String.fromCharCode('a'.charCodeAt(0)   i));
  }
  // 将大写字母A-Z加入到 baseChar 中
  for (let i = 0; i < 26; i  ) {
    array.push(String.fromCharCode('A'.charCodeAt(0)   i));
  }
  // 加入一些其它的ASCII中的打印字符(尽量对URL或者JSON友好)
  array.push('*', ' ', '-', '<', '=', '>');
  array.push('(', ')', '[', ']', '{', '}', '|');
  array.push('!', '$', '%', ':', ';', '@', '^', '_', '~');
  return array;
})();

// 进制转换/数字转字符
function scaleTransition(value: number | bigint, base = baseChar): string {
  if (value < 0) {
    throw new Error('scaleTransition Error, value < 0');
  }
  if (value === 0) {
    return base[0];
  }
  if (typeof value === 'bigint') {
    const radix = BigInt(base.length);
    let result = '';
    let resValue = value;
    while (resValue > 0) {
      result = base[Number(resValue % radix)]   result;
      resValue /= radix;
    }
    return result;
  } else {
    const radix = base.length;
    let result = '';
    let resValue = value;
    while (resValue > 0) {
      result = base[resValue % radix]   result;
      resValue = Math.floor(resValue / radix);
    }
    return result;
  }
}

function getPrefix(): string {
  // TODO 从服务端UUID前缀, 来避免多用户端生成的uuid出现重复, (如果获取失败,则使用时间戳来兜底)
  return String(Date.now());
}

// UUID参数集合, 使用 count 能保证在本地环境中UUID绝对不会重复.
const UuidObject: Record<string, { prefix: string; count: number }> = {};
function getUuidInfo(prefix: string): { prefix: string; count: number } {
  let uuidObject = UuidObject[prefix];
  if (uuidObject === undefined) {
    // 先使用MD5将 getPrefix 散列成为一个32位长度的16进制字符串, 再将其转换为bigInt数值, 最后使用scaleTransition转换来压缩长度
    const str1 = scaleTransition(BigInt(`0x${MD5(getPrefix())}`));
    // MD5散列理论上存在碰撞的可能性, 可以在加入一个一亿以内的随机数降低在散列发生碰撞时生成UUID重复的可能性
    const str2 = scaleTransition(Math.floor(Math.random() * 100000000));
    if (prefix) {
      uuidObject = {
        prefix: prefix   splitChar   str1   splitChar   str2   splitChar,
        count: 0,
      };
    } else {
      uuidObject = {
        prefix: str1   splitChar   str2   splitChar,
        count: 0,
      };
    }
    UuidObject[prefix] = uuidObject;
  }
  return uuidObject;
}

// 生成随机的UUID
export default function createUUID(prefix = '') {
  const uuidObject = getUuidInfo(prefix);
  return uuidObject.prefix   scaleTransition(uuidObject.count  );
}

// for (let i = 0; i < 10; i  ) {
//   console.log(createUUID());
// }

学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgghhah
系列文章
更多 icon
同类精品
更多 icon
继续加载