位操作

本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2021-07-03

JS中 十进制 和 二进制的转换

// 十进制 => 二进制
let num = 10;
console.log(num.toString(2)); // 1010

// 二进制 => 十进制
let num1 = 1001;
console.log(parseInt(num1, 2)); // 9

取整

按位或: 1 | 1 = 11 | 0 = 10 | 0 = 1

function toInt(num) {
  return num | 0;
}

判断奇偶

按位与: 1 & 1 = 11 & 0 = 00 & 0 = 0

// 奇数的二进制最后一位必然为1,所以任意一个奇数 & 1 一定等于1。
function fn(num) {
  return num & 1;
}x

权限管理

将权限使用 2^n 次方表示, 因为其对应的二进制为 00010010等等,只有一位为1,不同权限取不同位的1。

添加权限 按位或 即可,00100000 | 00000010 = 00100010

按位异或,00100010 ^ 00000010 = 00100000 / 00100000 ^ 0000010 = 00100010 更合适有就删除没有就添加这种场景。

删除权限为 00100010 & (~00000010) = 00100010 & 11111101 = 00100000

enum Permission {
  /** 读取权限 */
  READ = 0b0001,

  /** 修改权限 */
  WRITE = 0b0010,

  /** 删除权限 */
  DELETE = 0b0100,

  /** 新增权限  */
  ADD = 0b1000,
}


class UserPermission {

  /** 用户权限 */
  public userPermission: number;
  
  constructor(userCode?: number) {
    this.userPermission = userCode || 0;
  }

  addPermission(permission: Permission) {
    this.userPermission = this.userPermission | permission;
  }

  deletePermission(permission: Permission) {
    this.userPermission = this.userPermission & (~permission);
  }
}

const u = new UserPermission();

u.addPermission(Permission.READ);
console.log(u.userPermission); // 1
u.addPermission(Permission.DELETE);
console.log(u.userPermission); // 5
u.deletePermission(Permission.READ);
console.log(u.userPermission); // 4

上述方案的局限性就是,权限码只能是 1, 2, 4, 8,…,1024,… 那么最多只能有32中权限码,在较为复杂的场景就不适用了。

权限空间:

权限code, 使用 index, pos 来表示, index 表示权限空间, pos表示在二进制中1的位置,从0开始, pos为5, 则二进制为 00100000 = 2 ^ 5 = 32; 权限码2, 8 代表权限空间2中的 8 权限。

用户权限,如果有3个权限空间, 用户权限字符串为1, 17, 8, 说明对应的权限码是 0,11,11,42,3

enum Permission {
  /** 系统权限 */
  SYSTEM_PERMISSION = '0,0',
  /** 相册权限 */
  SYSTEM_PHOTO_PERMISSION = '0,1',
  /** 定位权限 */
  SYSTEM_POSITION_PERMISSION = '0,2',
  /** 麦克风权限 */
  SYSTEM_MICROPHONE_PERMISSION = '0,3',

  /** 数据库管理权限 */
  DATA_MANAGE = '1,0',
  /** 数据库管理权限 */
  DATA_ADD = '1,1',
  /** 数据库管理权限 */
  DATA_DELETE = '1,2',
  /** 数据库管理权限 */
  DATA_UPDATE = '1,3',
  /** 数据库管理权限 */
  DATA_GET = '1,4',

  /** 接口权限 */
  /** 数据库管理权限 */
  INTERFACE_ADD = '2,0',
  /** 数据库管理权限 */
  INTERFACE_DELETE = '2,1',
  /** 数据库管理权限 */
  INTERFACE_UPDATE = '2,2',
  /** 数据库管理权限 */
  INTERFACE_GET = '2,3',
}

interface HandlePermission {
  /** 当前需要操作的权限空间 */
  index: number;

  /** 用于生成当前权限的权限码 */
  pos: number;
  
  /** 当前权限空间已有的权限码是 */
  currentZoneCode: number;

  /** 当前用户权限字符串切割成的字符串数组 */
  userPermission: string[];
}


class UserPermission {

  /** 用户权限 */
  public userPermission: string;
  
  constructor(userCode?: string) {
    this.userPermission = userCode || '';
  }

  /** 增加权限 */
  addPermission(permission: Permission) {
    // currentZoneCode 指当前在 index 权限空间的权限字符串
    const { index, pos, currentZoneCode, userPermission } = this.handlePermission(permission);
    userPermission[index] = `${currentZoneCode | Math.pow(2, pos)}`
    this.userPermission = userPermission.join(',');

    // 举例
    // 如果当前权限为 4,8,1 ,添加权限 2,3
    // 这里的 index = 2, pos = 2, currentZoneCode = 1, userPermission = [4, 8, 1]
    // userPermission[2] = 1 | (2 << 3) = 9
    // userPermission = [4,8,9].join(',') 
    // 对应权限就是 0,2 、 1,3 、 2,0 、2,3
  }

  /** 删除权限 */
  deletePermission(permission: Permission) {
    const { index, pos, currentZoneCode, userPermission } = this.handlePermission(permission);
    userPermission[index] = `${currentZoneCode & (~Math.pow(2, pos))}`
    this.userPermission = userPermission.join(',');
  }

  /** 判断是否有该权限 */
  hasPermission(permission: Permission) {
    const { pos, currentZoneCode } = this.handlePermission(permission);
    const target = Math.pow(2, pos)
    return (currentZoneCode & target) === target;

    // 举例
    // 如果当前权限为 7,8,1 ,判断权限 0,4
    // 这里的 currentZoneCode = 7, pos = 4; 
    // 7 & 16 === 16 的比较是 false
    // 如果判断 0,2 结果就是 7 & 4 = 4 就是 true; 7代表着 0,0 0,1 0,2  
  }

  /** 处理 Permission 为 数字型数组 */
  handlePermission(permission: Permission): HandlePermission {
    const [index, pos] = permission.split(",");

    // 取出当前的用户权限转换为 字符串数组
    const userPermission: string[] = this.userPermission ? this.userPermission.split(",") : [];

    // 根据 index 取出 当前用户在index空间所拥有的权限,然后进行添加权限运算
    const currentZoneCode = Number(userPermission[+index] || 0);

    return {
      index: +index,
      pos: +pos,
      currentZoneCode: currentZoneCode,
      userPermission: userPermission,
    }
  }

  /** 根据 权限码 还原权限 */

  togglePermission() {
    const results: string[] = [];
  
    if (!this.userPermission) {
      return results;
    }
  
    Object.keys(Permission).forEach((key) => {
      const value: Permission = (Permission as any)[key]
      if (this.hasPermission(value)) {
        results.push(key)
      }
    })
  
    return results;
  }

  /** log 打印当前所拥有的权限 */
  log() {
    console.log('当前userPermission: %s', this.userPermission);
    console.log('当前权限为: %s', this.togglePermission().join(','));
  }
}

const u = new UserPermission();
u.addPermission(Permission.DATA_ADD);
u.log();
// 当前userPermission: ,2
// 当前权限为: DATA_ADD
u.addPermission(Permission.SYSTEM_MICROPHONE_PERMISSION);
u.log();
// 当前userPermission: 8,2
// 当前权限为: SYSTEM_MICROPHONE_PERMISSION,DATA_ADD
u.addPermission(Permission.INTERFACE_UPDATE);
u.log();
// 当前userPermission: 8,2,4
// 当前权限为: SYSTEM_MICROPHONE_PERMISSION,DATA_ADD,INTERFACE_UPDATE


u.deletePermission(Permission.INTERFACE_UPDATE);
u.log();
// 当前userPermission: 8,2,0
// 当前权限为: SYSTEM_MICROPHONE_PERMISSION,DATA_ADD

const has1 = u.hasPermission(Permission.INTERFACE_ADD);
console.log(has1); // false

const has2 = u.hasPermission(Permission.SYSTEM_MICROPHONE_PERMISSION);
console.log(has2); // true

切换 0 和 1

/** num为 0 / 1 */
function toggle(num) {
  return num ^ 1;
}

判断数组中某项是否存在

if (~arr.indexOf(item)) { // 存在
  // code
}

左移

对任一数值 x 进行左移n位,等同于 x * 2^n

3 << 2 = 12

右移

对任一数值 x 进行右移n位,等同于 x / 2^n,然后取整

64 >> 2 = 16

十六进制和 rgb 的互相转换


// rgb(255, 255, 255) => '#ffffff'
// 先将rgb转换为 十进制,再使用toString(16)转换为十六进制
// 第三个 255 => 0000ff
// 第二个 255 => 255 * 16^2 => 00ff00  16^2 = (2^4)^2 = 2^8
// 第二个 255 => 255 * 16^4 => ff0000

// 由于 | 运算在二进制对位在同步补位的时候就相当于是 累加

function rgbToHex(rgb){
  // 取出rgb中的数值
  let arr = rgb.match(/\d+/g);
  if (!arr || arr.length !== 3) {
    console.error('rgb数值不合法');
    return
  }
  let hex = (arr[0] << 16 | arr[1] << 8 | arr[2]).toString(16);
  return `#${hex.padStart(6, '0')}`;
}

// ffffff = 16777215
// 16777215 的 二进制 111111111111111111111111 共24位
// 当右移16位,即前8位的十进制。
// 当右移8位,即前16位的十进制,在跟 0xff = 255 = 1111111 进行与运算,即取出这16位中的后8位(即中间8位的)十进制
// 当直接与 0xff进行与运算,即取出24位中的后8位的十进制
function hexToRgb(hex) {
  let num = hex.replace('#', '0x');
  let r = num >> 16;
  let g = num >> 8 & 0xff;
  let b = num & 0xff;    
  return `rgb(${r},${g},${b})`;
}