从 (97 & ~32) == 65 //true 入手js的位操作符

文章

先来个列表

操作符     用法      描述
按位与     a & b     如果两个操作数对应位都是 1 的话则在该位返回 1。
按位或     a | b     如果两个操作数对应位都是 0 的话则在该位返回 0。
按位异或   a ^ b     如果两个操作数对应位只有一个 1 的话则在该位返回 1。
求反       ~ a       反转操作数的每一位。
左移       a << b    将 a 的二进制形式左移 b 位。右面的空位补零。
算术右移   a >> b       将 a 的二进制形式右移 b 位。忽略被移出的位。
逻辑右移   a >>> b    将 a 的二进制形式右移 b 位。忽略被移出的位,左侧补入 0。

先知道一点:

数据在内存中是以二进制形式存放的。数值是以补码表示的。一个正数的补码和其原码的形式相同。
而负数的补码方式是将其绝对值的二进制形式“按位求反再加1” 

二元位操作符

用作测试的函数

扔到console里直接用即可

var hex10to2 = function (o){
    //鉴于parseInt('0101') 与 parseInt(0101) 的不同结果(纠结,各浏览器应该不同,待测试),
    //在您输入的时候请直接输入8进制数时,请直接输入数字(0101)
    //测试用,所以先只支持整数

    if(parseInt(o)){
        return parseInt(o).toString(2);
    } else{
        console.log('参数不正确,正确的参数为:1.整数 2.合法字符串(如"123","0123","0x123")');
    }   
}

//忽略符号处理
var hex2to10 = function(str){
    //TODO:判断是否只包括 1 , 0
    return parseInt(str,2);
}
//两个函数的合并作业待做

我还比较熟悉的: &,|,^ 三种,举例如下:

65 & 32 结果为 0

1000001
 100000
-------
0000000

65 | 32 结果为 97

1000001
 100000
-------
1100001

65 ^ 32 结果为 97

1000001
 100000
-------
1100001

另外~很少用,也不熟悉

详细说明

取反运算符~对运算数按位取反,将运算数各位值由1变为0,或由0变为1.因为计算机中数值以带符号位二进制表示,所以取反后,最高位符号位发生变化。

32按位取反后结果为(-33)。我们采用32的8位二进制形式:

hex10to2(32) => 100000 补全得:00100000

00100000 按位取反为 11011111,这是一个补码形式,最高位为符号位。将该补码再求补(符号位保持不变,其他位按位取反,最后加1),得到10100001,该值是带符号的真值

hex2to10('100001'); //33

加个负号得:-33

另外:

97 & -33 //65

将 -33(10100001)符号位保持不变,其他位按位取反,最后加1,为:11011111

01100001
11011111
--------
01000001

hex2to10('01000001'); //65

正如你看到的,原码和补码是可逆的, 写成

97 & ~32

更一目了然一些,因为我只需要去掉从右向左数的第6个 1,那个1 => 2的(6-1)次方,所以 & 一下 ~32 即可

位操作进行加减运算

... 64 32 16 8 4 2 1
...  7  6  5 4 3 2 1

如果想让第x位变成 1,也就是加上 2 的 x-1 次方(如果那位不是1的话),令 y = 2 的 x-1 次方

x | y

如果想让第x位变成 0,也就是减去 2 的 x-1 次方(如果那位不是0的话),令 y = 2 的 x-1 次方

x & ~ y

有意思的实现

手动大小写

因为javascript已经实现了(toUpperCase() and toLowerCase())

function isString(value){return typeof value == 'string';}

var manualLowercase = function(s) {
    return isString(s)
            ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
            : s;
};
var manualUppercase = function(s) {
    return isString(s)
            ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
            : s;
};

奇偶数判断

function isNumber(value){return typeof value == 'number';}

//利用 0 或 1 对 1 的 & 运算都是原数值,可得:
var isOdd = function(num){ //奇数
    if(isNumber(num)){
        return num & 1 ? true : false;
    } else{
        return false;
    }
}

var isEven = function(num){ //偶数           
    if(isNumber(num)){
        return num & 1 ? false : true;
    } else{
        return false;
    }
}

移位操作符

移位操作符需要两个操作数:第一个是要进行移位的数值,第二个指定要对第一个数移位的数目。移位的方向由使用的操作符决定。

移位操作符将把两个操作符转换为 32 位整型数值,并返回与左操作数类型相同的结果。

<< (左移)

该操作符将把第一个操作数向左移若干位。移出的位将被忽略。右侧空位补零。 例如,9<<2 结果为 36,因为 1001 向左移两位变成 100100,这是 36。快捷算法:9 * (2的移位次方)

>> (算术右移)

该操作符将把第一个操作数向右移若干位。移出的位将被忽略。左侧的空位补上与原来最左面位相同的值。 例如,9>>2 结果为 2,因为 1001 右移两位变成 10,这是 2。。快捷算法:9 / (2的移位次方)

反之,-9>>2 结果为 -3,因为要考虑到符号位。

9在计算机中表示为(我们采用9的8位二进制形式)

00001001

则 -9,为:

10001001

重复上面的算法:符号位保持不变,其他位按位取反,最后加1,得到 -9 在计算机中的表示

11110111

算数右移两位 => 补1

11111101

重复补码算法

10000011

得到 -3

>>> (逻辑右移)

该操作符将把第一个操作数向右移若干位。移出的位将被忽略。左侧的空位补零。 例如,19>>>2 结果为 4,因为 10011 右移两位变成 100,这是 4。对于非负数,算术右移和逻辑右移结果相同。

那么负数呢?

-9>>2 结果为 ?

9在计算机中表示为(我们采用9的8位二进制形式)

00001001

则 -9,为:

10001001

重复上面的算法:符号位保持不变,其他位按位取反,最后加1,得到 -9 在计算机中的表示

11110111

逻辑右移两位 => 补0

00111101

因为变成正数了,所以不用重复补码算法了

计算机采用的是32位二进制形式,所以

0011...1101 //中间是30个1

得到 1073741821

hex10to2(1073741821) //111111111111111111111111111101
"111111111111111111111111111101".length //30,略去了前面两个0

Reference

原码,反码,补码

« Node.js的调试 菜鸟学习linux »

留下一句吧