前端使用位掩码实现权限以及处理不定参数

246 阅读3分钟

前端权限

前端权限一直都是备受关注的技术点.通过给项目引入了权限控制方案,可以满足我们灵活的调整用户访问或者操作的许可,我们可以将前端权限分为定义权限以及判断权限两步

举个栗子

当我们需要去处理一个权限的时候

例如我们需要给一个创建按钮添加权限的时候

定义权限

canCreated=false|true

判断权限

if(canCreated){
  console.log('可以创建!')
}
else{
  console.log('无法创建!')
}

当我们需要去处理多个权限的时候

当然我们日常开发中,需要更多考虑的是多个权限的时候如何去定义和判断权限

通过定义权限点实现权限

定义权限: 定义创建、删除、更新、查看按钮对应的权限点,并给该用户定义相关的权限

const canCreated=1001
const canDelete=1002
const canUpdate=1003
const canLook=1001

判断权限:

const userPermissionList=[1001,1003]

此时通过权限数组可以知道用户拥有创建和更新的权限

思考

但是不管是通过变量去定义或者是权限点去定义,都需要多个变量才能表达这个用户的权限, 如果我们为每种有可能出现的权限都写一个变量来标识, 那我们需要写 2 ^ 4 = 16 个变量还储存这些组合,如何通过一个变量去表达这个用户的权限尼?

通过位掩码 二进制实现权限

首先进行名词解释,什么是”位掩码“。位掩码(BitMask),是”位(Bit)“和”掩码(Mask)“的组合词。”位“指代着二进制数据当中的二进制位,而”掩码“指的是一串用于与目标数据进行按位操作的二进制数字。组合起来,就是”用一串二进制数字(掩码)去操作另一串二进制数字“的意思。明白了位掩码的作用以后,我们就可以通过它来对权限集二进制数进行操作了。

定义权限:

  • 按位左移 创建对应二进制
const canCreated=1   //0b0001   1<<0
const canDelete=2   //0b0010    1<<1
const canUpdate=4  //0b0100     1<<2
const canLook=8   //0b1000      1<<3
  • 按位或代表的就是多种处理方式
  • 按位与来判断是否包含当前处理方式

当用户拥有【创建】、【删除】、【查看】权限的时候:

通过二进制按位或 代表多种方式( 如果相对应位都是0,则结果为0,否则为1 ):&

此时可以通过11来代表该用户拥有【创建】、【删除】、【查看】的权限

判断权限:

上文已经定义了用户拥有【创建】、【删除】、【查看】的权限,又如何判断是否用户某个权限尼?

通过二进制按位与 来判断是否包含当前处理方式( 如果相对应位都是1,则结果为1,否则为0 ):|

如上图可以判断用户拥有创建的权限,不拥有更新的权限

二进制数字各种运算!<<<、 >>> 、|=、&、^区别

处理不定参数

当然我们还能通过位掩码来做更多的操作,比如在函数库lodash 中有下面的代码:

baseClone

/**
 * The base implementation of `clone` and `cloneDeep` which tracks
 * traversed objects.
 *
 * @private
 * @param {*} value The value to clone.
 * @param {number} bitmask The bitmask flags.
 *  1 - Deep clone
 *  2 - Flatten inherited properties
 *  4 - Clone symbols
 * @param {Function} [customizer] The function to customize cloning.
 * @param {string} [key] The key of `value`.
 * @param {Object} [object] The parent object of `value`.
 * @param {Object} [stack] Tracks traversed objects and their clone counterparts.
 * @returns {*} Returns the cloned value.
 */
function baseClone(value, bitmask, customizer, key, object, stack) {
  let result
  const isDeep = bitmask & CLONE_DEEP_FLAG
  const isFlat = bitmask & CLONE_FLAT_FLAG
  const isFull = bitmask & CLONE_SYMBOLS_FLAG

  if (customizer) {
    result = object ? customizer(value, key, object, stack) : customizer(value)
  }
  if (result !== undefined) {
    return result
  }
  if (!isObject(value)) {
    return value
  }
  const isArr = Array.isArray(value)
  const tag = getTag(value)
  if (isArr) {
    result = initCloneArray(value)
    if (!isDeep) {
      return copyArray(value, result)
    }
  } else {
    const isFunc = typeof value === 'function'

    if (isBuffer(value)) {
      return cloneBuffer(value, isDeep)
    }
    if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
      result = (isFlat || isFunc) ? {} : initCloneObject(value)
      if (!isDeep) {
        return isFlat
          ? copySymbolsIn(value, copyObject(value, keysIn(value), result))
          : copySymbols(value, Object.assign(result, value))
      }
    } else {
      if (isFunc || !cloneableTags[tag]) {
        return object ? value : {}
      }
      result = initCloneByTag(value, tag, isDeep)
    }
  }
  // Check for circular references and return its corresponding clone.
  stack || (stack = new Stack)
  const stacked = stack.get(value)
  if (stacked) {
    return stacked
  }
  stack.set(value, result)

  if (tag == mapTag) {
    value.forEach((subValue, key) => {
      result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack))
    })
    return result
  }

  if (tag == setTag) {
    value.forEach((subValue) => {
      result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack))
    })
    return result
  }

  if (isTypedArray(value)) {
    return result
  }

  const keysFunc = isFull
    ? (isFlat ? getAllKeysIn : getAllKeys)
    : (isFlat ? keysIn : keys)

  const props = isArr ? undefined : keysFunc(value)
  arrayEach(props || value, (subValue, key) => {
    if (props) {
      key = subValue
      subValue = value[key]
    }
    // Recursively populate clone (susceptible to call stack limits).
    assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack))
  })
  return result
}

cloneDeep文件中:

// 定义几种状态
const CLONE_DEEP_FLAG = 1
const CLONE_SYMBOLS_FLAG = 4

function cloneDeep(value) {
  return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG)
}

此时:

function baseClone(value, bitmask, customizer, key, object, stack) {
  // 0101 & 0001 => 0001 => 之后用的时候 if(isDeep) 会隐式转换成 true
  const isDeep = bitmask & CLONE_DEEP_FLAG
  // 0101 & 0010 => 0000 => 之后用的时候 if(isFlat) 会隐式转换成 false
  const isFlat = bitmask & CLONE_FLAT_FLAG
  // 0101 & 0100 => 0100 => 之后用的时候 if(isFull) 会隐式转换成 true
  const isFull = bitmask & CLONE_SYMBOLS_FLAG
}