用户权限还能这么玩,颠覆你的认知(一)

642 阅读8分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

本文同时参与 「掘力星计划」  ,赢取创作大礼包,挑战创作激励金

欢迎来到掘金,这里是小小切图仔_可笑可笑😁😁。

最近跟我们公司后台做一个项目,发现了一个好玩的东西哈,真的以前没想到的,也有可能是我们太笨了,哈哈哈哈。不过现在用到也不晚,可能是老技术了,我也不知道,知道的人可以跟我说一下,是不是以前的人都是这样用的。

那这个是什么技术呢!!!它就是利用进制位运算来做用户的权限管理。非常好用,下面我分三个议题来讲。

  1. javaScript的进制位运算
  2. 权限的判断
  3. 用后感

一· 进制的位运算

我们需要对进制的位运算有一定的了解,不管是二进制还是十六进制都需要我们了解,不过庆幸的是两个进制位运算是一样的。

我在用这个位运算的时候也是绞尽脑汁想了一天,最后发现使用的方法很简单,因为js已经帮你集成好了用法,这让我们用起来会更加方便。我是那种用一种技术就必须搞明白原理的人,不然不清不楚用一种技术,后期定位问题也很难,所以希望大家也是一样的。位运算我脑补了很多资料,看了很多博客的资料。

废话不多说,直接整起。。。。

1. JavaScript 位运算

1.1. Number

在讲位运算之前,首先简单看下 JavaScript 中的 Number,下文需要用到。

JavaScript 里,数字均为基于 IEEE 754 标准的双精度 64 位的浮点数,引用某乎的图片,它的结构长这样:

image.png

sign bit(符号): 用来表示正负号

exponent(指数): 用来表示次方数

mantissa(尾数): 用来表示精确度

也就是说一个数字的范围只能在 -(2^53 -1) 至 2^53 -1 之间。

这里我们顺便讲一下精度丢失的问题,0.1 + 0.2 算不准的原因。浮点数用二进制表达时是无穷的,且最多 53 位,必须截断,进而产生误差。最简单的解决办法就是放大一定倍数变成整数,计算完成后再缩小。或者大家都有更好的方法解决。

在此之外还有四种数字进制,有些进制是我们与计算机沟通的最终形态:

四种数字进制分别为:十进制、十六进制、八进制、二进制。

  • 十进制:没有前导0的数值。这个也是我们经常用到
  • 八进制:有前缀0o0O的数值,或者有前导0、且只用到0-7的八个阿拉伯数字的数值。
  • 十六进制:有前缀0x0X的数值。
  • 二进制:有前缀0b0B的数值。

下面是一些例子。

// 十进制
 
123456789
 
0
 
 
// 二进制:前缀 0b,0B
 
0b10000000000000000000000000000000 // 2147483648
 
0b01111111100000000000000000000000 // 2139095040
 
0B00000000011111111111111111111111 // 8388607
 
 
// 八进制:前缀 0o,0O(以前支持前缀 0)
 
0o755 // 493
 
0o644 // 420
 
 
// 十六进制:前缀 0x,0X
 
0xFFFFFFFFFFFFFFFFF // 295147905179352830000
 
0x123456789ABCDEF // 81985529216486900
 
0XA // 10

我们 JavaScriptNumber 就讲这么多。想了解更多的可以去看百度维基百科

1.2. 位运算

w3c是这么解释javasCript 位运算的:

JavaScript 使用 32 位按位运算数

JavaScript 将数字存储为 64 位浮点数,但所有按位运算都以 32 位二进制数执行。

在执行位运算之前,JavaScript 将数字转换为 32 位有符号整数。

执行按位操作后,结果将转换回 64 位 JavaScript 数。

简单来说就是:按位操作符将其操作数当作 32 位的比特序列(由 0 和 1 组成)操作,返回值依然是标准的 JavaScript 数值。

JavaScript 中位运算操作符有

运算符用法描述
按位与(AND)a & b对于每一个比特位,只有两个操作数相应的比特位都是 1 时,结果才为 1,否则为 0。
按位或(OR)a | b对于每一个比特位,当两个操作数相应的比特位至少有一个 1 时,结果为 1,否则为 0。
按位异或(XOR)a ^ b对于每一个比特位,当两个操作数相应的比特位有且只有一个 1 时,结果为 1,否则为 0。
按位非(NOT)~a反转操作数的比特位,即 0 变成 1,1 变成 0。
左移(Left shift)a << b将 a 的二进制形式向左移 b (< 32) 比特位,右边用 0 填充。
有符号右移a >> b将 a 的二进制表示向右移 b (< 32) 位,丢弃被移出的位。
无符号右移a >>> b将 a 的二进制表示向右移 b (< 32) 位,丢弃被移出的位,并使用 0 在左侧填充。

下面通过一些实例来加深对位运算的理解。

1.2.1 按位与(&)

&&运算符我们都知道,只有两个都为真,结果才为真。&道理是一样的,只有两个数的值为1时,才返回1。例如1和3的按位与操作:

				    0001
				 &  0011
				---------
				    0001

只有对应的数为1时,结果才为1,其他都为0。
判断一个数是奇数还是偶数,我们会用求余数来判断:

function assert(n) { 
    if (n % 2 === 1) { 
        console.log("n是奇数"); 
    } else { 
        console.log("n是偶数"); 
    } 
}
assert(3); // "n是奇数"

我们也可以用一个数和1进行按位&操作来判断,而且速度更快:

function assert(n) { 
    if (n & 1) { 
        console.log("n是奇数");
    } else {
        console.log("n是偶数"); 
    } 
}
assert(3); // "n是奇数"

下面是位运算过程:

				 1 = 0001
				 3 = 0011
				 --------
			         & = 0001

奇数的二进制码的最后一位数肯定是1,而1只有最后一位为1,按位&操作之后,结果肯定只有最后一位数为1。而偶数的二进制表示的最后一位数是0,和1进行按位&操作,结果所有位数都为0。

1.2.2 按位或(|)

|与||操作符的道理也是一样的,只要两个数中有一个数为1,结果就为1,其他则为0。

                                   0001 
                                 | 0011
                              ---------
                                   0011

对浮点数向下求整,我们会用下面的方法:

var num = Math.floor(1.1); // 1

我们也可以用位运算来求整:

var num = 1.1 | 0; // 1

其实浮点数是不支持位运算的,所以会先把1.1转成整数1再进行位运算,就好像是对浮点数向下求整。所以1|0的结果就是1。

1.2.3 按位非(~)

按位非就是求二进制的反码:

var num = 1; // 二进制 00000000000000000000000000000001 
var num1 = ~num; // 二进制 11111111111111111111111111111110

我们知道,js中的数字默认是有符号的。有符号的32位二进制的最高位也就是第一位数字代表着正负,1代表负数,0代表整数。那到底11111111111111111111111111111110等于多少呢?最高位为1代表负数,负数的二进制转化为十进制:符号位不变,其他位取反加1。取反之后为10000000000000000000000000000001,加1之后为10000000000000000000000000000010,十进制为-2。

1.2.4 按位异或(^)

按位异或是两个数中只有一个1时返回1,其他情况返回0。

                          0001 
                        ^ 0011 
                     --------- 
                          0010

数字与数字本身按位异或操作得到的是0,因为每两个对应的数字都相同,所以最后返回的都是0。

我们经常会需要调换两个数字的值:

var num1 = 1, num2 = 2, temp; 
temp = num1;
num1 = num2; // 2 
num2 = temp; // 1

如果装逼一点的话,可以这样:

var num1 = 1, num2 = 2;
num1 = [num2, num2 = num1][0];
console.log(num1); // 2
console.log(num2); // 1

如果想再装的稳一点的话,可以这样:

var num1 = 1, num2 = 2; 
num1 ^= num2; // num1 = num1 ^ num2 = 1 ^ 2 = 3
num2 ^= num1; // num2 = num2 ^ (num1 ^ num2) = 2 ^ (1 ^ 2) = 1 
num1 ^= num2; // num1 = num1 ^ num2 = 3 ^ 1 = 2
console.log(num1); // 2 
console.log(num2); // 1

结束但又没有完全结束

东西有点多,这一篇我们暂时先说到这里,还没结束,下一篇我们会继续讲。感兴趣的小伙伴可以继续往下看用户权限还能这么玩,颠覆你的认知(二)

创作不易,请多多支持,点点赞、点点关注与收藏,就是对我最大的支持,感谢观看我的文章,这里是小小切图仔_可笑可笑