3.5.2 位操作符

3.5.2 位操作符

操作符用于数值的底层操作,也就是操作内存中表示数据的比特(位)。ECMAScript中的所有数值都以 IEEE 754 64 位格式存储,但位操作并不直接应用到 64 位表示,而是先把值转换为32 位整数,再进行位操作,之后再把结果转换为 64 位。对开发者而言,就好像只有 32 位整数一样,因为 64 位整数存储格式是不可见的。

有符号整数使用 32 位的前 31 位表示整数值。第 32 位表示数值的符号,如 0 表示正,1 表示负。这
一位称为符号位(sign bit),它的值决定了数值其余部分的格式。

负值以一种称为二补数(或补码)的二进制编码存储。

一个数值的二补数通过如下 3 个步骤计算得到:
(1) 确定绝对值的二进制表示(如,对于 -18,先确定 18 的二进制表示);
(2) 找到数值的一补数(或反码),换句话说,就是每个 0 都变成 1,每个 1 都变成 0;
(3) 给结果加 1。
基于上述步骤确定 -18 的二进制表示,首先从 18 的二进制表示开始:

0000 0000 0000 0000 0000 0000 0001 0010
// 然后,计算一补数,即反转每一位的二进制值:
1111 1111 1111 1111 1111 1111 1110 1101
// 最后,给一补数加 1:
1111 1111 1111 1111 1111 1111 1110 1101
1
----------------------------------------------
1111 1111 1111 1111 1111 1111 1110 1110

那么,-18 的二进制表示就是 11111111111111111111111111101110。要注意的是,在处理有符号整数时,我们无法访问第 31 位。
ECMAScript 会帮我们记录这些信息。在把负值输出为一个二进制字符串时,我们会得到一个前面
加了减号的绝对值,如下所示:

let num = -18;
console.log(num.toString(2)); // "-10010"

在将 -18 转换为二进制字符串时,结果得到 -10010。转换过程会求得二补数,然后再以更符合逻辑
的形式表示出来。

在对 ECMAScript 中的数值应用位操作符时,后台会发生转换:64 位数值会转换为 32 位数值,然后执行位操作,最后再把结果从 32 位转换为 64 位存储起来。整个过程就像处理 32 位数值一样,这让二进制操作变得与其他语言中类似。但这个转换也导致了一个奇特的副作用,即特殊值 NaN 和 Infinity 在位操作中都会被当成 0 处理。

如果将位操作符应用到非数值,那么首先会使用 Number() 函数将该值转换为数值(这个过程是自动的),然后再应用位操作。最终结果是数值。

  1. 按位非( ~ )

    它的作用是返回数值的一补数。

    按位非是 ECMAScript 中为数不多的几个二进制数学操作符之一。

    let num1 = 25; // 二进制 00000000000000000000000000011001
    let num2 = ~num1; // 二进制 11111111111111111111111111100110
    console.log(num2); // -26
    

    这里,按位非操作符作用到了数值 25,得到的结果是 -26。由此可以看出,按位非的最终效果是对数值取反并减 1,就像执行如下操作的结果一样:

    let num1 = 25;
    let num2 = -num1 - 1;
    console.log(num2); // "-26"
    

    实际上,尽管两者返回的结果一样,但位操作的速度快得多。这是因为位操作是在数值的底层表示上完成的。

  2. 按位与( & )

    有两个操作数。

    本质上,按位与就是将两个数的每一个位对齐,然后基于真值表中的规则,对每一位执行相应的与操作。

    第一个数值的位  第二个数值的位  结 果
        1            1           1
        1            0           0
        0            1           0
        0            0           0
    

    按位与操作在两个位都是 1 时返回 1,在任何一位是 0 时返回 0。

    let result = 25 & 3;
    console.log(result); // 1
    

    25 和 3 的按位与操作的结果是 1。

     25 = 0000 0000 0000 0000 0000 0000 0001 1001
      3 = 0000 0000 0000 0000 0000 0000 0000 0011
    ---------------------------------------------
    AND = 0000 0000 0000 0000 0000 0000 0000 0001
    

    如上所示,25 和 3 的二进制表示中,只有第 0 位上的两个数都是 1。于是结果数值的所有其他位都会以 0 填充,因此结果就是 1。

  3. 按位或( | )

    有两个操作数。

    第一个数值的位  第二个数值的位  结 果
        1            1           1
        1            0           1
        0            1           1
        0            0           0
    

    按位或操作在至少一位是 1 时返回 1,两位都是 0 时返回 0。

    let result = 25 | 3;
    console.log(result); // 27
    

    可见 25 和 3 的按位或操作的结果是 27:

    25 = 0000 0000 0000 0000 0000 0000 0001 1001
     3 = 0000 0000 0000 0000 0000 0000 0000 0011
    ---------------------------------------------
    OR = 0000 0000 0000 0000 0000 0000 0001 1011
    

    在参与计算的两个数中,有 4 位都是 1,因此它们直接对应到结果上。二进制码 11011 等于 27。

  4. 按位异或( ^ )

    有两个操作数。

    第一个数的位  第二个数的位  结 果
        1            1        0
        1            0        1
        0            1        1
        0            0        0
    

    按位异或与按位或的区别是,它只在一位上是 1 的时候返回 1(两位都是 1 或 0,则返回 0)。

    let result = 25 ^ 3;
    console.log(result); // 26
    

    25 和 3 的按位异或操作结果为 26,如下所示:

     25 = 0000 0000 0000 0000 0000 0000 0001 1001
      3 = 0000 0000 0000 0000 0000 0000 0000 0011
    ---------------------------------------------
    XOR = 0000 0000 0000 0000 0000 0000 0001 1010
    

    两个数在 4 位上都是 1,但两个数的第 0 位都是 1,因此那一位在结果中就变成了 0。其余位上的 1在另一个数上没有对应的 1,因此会直接传递到结果中。二进制码 11010 等于 26。(注意,这比对同样两个值执行按位或操作得到的结果小 1。)

  5. 左移( << )

    会按照指定的位数将数值的所有位向左移动。

    比如,如果数值 2(二进制 10)向左移 5 位,就会得到 64(二进制 1000000)

    let oldValue = 2; // 等于二进制 10
    let newValue = oldValue << 5; // 等于二进制 1000000,即十进制 64
    

    注意在移位后,数值右端会空出 5 位。左移会以 0 填充这些空位,让结果是完整的 32 位数值。

    注意,左移会保留它所操作数值的符号。比如,如果 -2 左移 5 位,将得到 -64,而不是正 64。

  6. 有符号右移( >> )

    会将数值的所有 32 位都向右移,同时保留符号(正或负)。
    有符号右移实际上是左移的逆运算。

    let oldValue = 64; // 等于二进制 1000000
    let newValue = oldValue >> 5; // 等于二进制 10,即十进制 2
    

    同样,移位后就会出现空位。不过,右移后空位会出现在左侧,且在符号位之后。
    ECMAScript 会用符号位的值来填充这些空位,以得到完整的数值。

  7. 无符号右移( >>> )

    会将数值的所有 32 位都向右移。

    对于正数,无符号右移与有符号右移结果相同。64 向右移动 5 位,会变成 2;

    let oldValue = 64; // 等于二进制 1000000
    let newValue = oldValue >>> 5; // 等于二进制 10,即十进制 2
    

    对于负数,有时候差异会非常大。与有符号右移不同,无符号右移会给空位补 0,而不管符号位是
    什么。对正数来说,这跟有符号右移效果相同。但对负数来说,结果就差太多了。无符号右移操作符将负数的二进制表示当成正数的二进制表示来处理。因为负数是其绝对值的二补数,所以右移之后结果变得非常之大:

    let oldValue = -64; //  等于二进制 11111111111111111111111111000000
    let newValue = oldValue >>> 5; // 等于十进制 134217726
    

    在对 -64 无符号右移 5 位后,结果是 134 217 726。这是因为 -64 的二进制表示是 11111111111111111111111111000000
    无符号右移却将它当成正值,也就是
    4 294 967 232
    把这个值右移 5 位后,结果是
    00000111111111111111111111111110,即 134 217 726。

学习下一篇文章

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容