Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

巧用 JAVASCRIPT 中的位运算 #63

Open
YIXUNFE opened this issue Mar 17, 2016 · 8 comments
Open

巧用 JAVASCRIPT 中的位运算 #63

YIXUNFE opened this issue Mar 17, 2016 · 8 comments

Comments

@YIXUNFE
Copy link
Owner

YIXUNFE commented Mar 17, 2016

巧用 JAVASCRIPT 中的位运算

最近边忙边啃朴神写的《深入浅出 Node.js》,在讲述 Node.js 中模块机制的章节中看到了一段话:

Javascript 的一个典型弱点就是位运算。Javascript 的位运算参照 Java 的位运算实现,但是 Java 位运算是在 int 型数字的基础上进行的,而 Javascript 中只有 double 型的数据类型,在进行位运算的过程中,需要将 double 型转换为 int 型,然后在进行。所以,在 Javascript 层面上做位运算的效率不高。

在书中,这段话告诉我们,为了提高模块的性能,在频繁出现位运算的需求时,我们可以使用 C/C++ 编写模块以提高性能(相对于直接用 Javascript 写)。然而这段话倒是激发了我对 Javascript 位运算的兴致。

虽然平时也常常用 ~~ 做向上取整等操作,但终究对位运算还是有点生疏,所以利用这个机会,再次温习一遍位运算。


## 与(&)运算

& 运算与我们熟悉的 && 运算类似,两个都是 1 才会得到 1。比如 2 & 3:

十进制 二进制
2 10
& 3 11
= 2 10

运算后结果就是 2 了。


### 巧用——判断奇偶数

利用 & 运算的特点,我们可以用以简单的判断奇偶数,公式:

n & 1 === 0 //true 为 奇数

奇偶数的判断其实就是判断数字的二进制值最后一位是 0 还是 1。所以利用 & 运算得到数字的最后一位值即可。

比如 11 & 1 = 1

十进制 二进制
11 1011
& 1 0001
= 1 0001

## 或(|)运算

| 运算与我们熟悉的 || 运算类似,两个中有一个是 1 就会得到 1。比如 2 & 3:

十进制 二进制
2 10
3
= 3 11

运算后结果就是 3 了。


### 巧用——向下取整

通常我们会使用 Math.floor 对一个数字向下取整,但是利用 | 运算也可以做到。

(3.14 | 0) === 3 //true

这里面其实做了两步操作,由于浮点数是不支持位运算的,所以会先把 3.14 转成整数 3 再进行位运算。所以 3.14 | 0 的结果就是 3。


## 非(~)运算

~ 运算的特点是将所有位取反,包括符号位。比如 ~3:

十进制 二进制
~ 3 00000000000000000000000000000011
= -4 11111111111111111111111111111100

最高位表示符号位,最高位为 1 表示是负数。负数的二进制转化为十进制的规则是,符号位不变,其他位取反后加 1。取反之后为 10000000000000000000000000000011,加1之后为 10000000000000000000000000000100,所以十进制为 -4。


### 巧用——向上取整

我们可以利用两次 ~ 运算来取代 Math.floor 方法。

Math.floor(3.14) //3
~~3.14 //3

感谢 @chechecarer 同学的指正 👍


## 异或(^)运算

^ 运算的规则是两个数中只有一个为 1 时才得到 1,其他情况得到 0,比如 2 ^ 3:

十进制 二进制
2 10
^ 3 11
= 1 01

### 巧用——交替变量
var a = 1, b = 2;
a ^= b; // a = a ^ b = 1 ^ 2 = 3
b ^= a; // b = b ^ (a ^ b) = 2 ^ (1 ^ 2) = 1
a ^= b; // a = a ^ b = 3 ^ 1 = 2

资料整理到这里自己有点晕 😂 ,两个变量在不使用第三个变量的前提下交换,还有其他多种方法。比如:

var a = 1, b = 2;
a = a + b; // 3
b = a - b; // 1
a = a - b; // 2

好像更加直观点 😂 。


## 左移(<<)运算

<< 运算即将数字二进制值左移,多出的位数以 0 补位,并不影响符号位。比如 3 << 2

十进制 二进制
3 11
<< 2
= 12 1100

### 巧用——求 2 的 N 次方
function power(n) {
    return 1 << n;
}
power(4); // 16

以 2 为底数,左移的位数即底数的次方。


## 右移(>>)运算

运算即将数字二进制值右移,多出的位数以 0 补位,并不影响符号位。比如 10 >> 2

十进制 二进制
10 1010
>> 2
= 2 10

### 巧用——求一个数字的 N 等分(向下取整)
function half(num, n) {//平分 n 次
    return num >> n;
}
half(4, 1); // 2
half(4, 2); // 1
half(5, 1); // 2

## 无符号右移(>>>)运算

正数的无符号右移与有符号右移结果是一样的。负数的无符号右移会把符号位也一起移动,而且无符号右移会把负数的二进制码当成正数的二进制码:

var num = -64; // 11111111111111111111111111000000
num = num >>> 5; // 134217726

### 巧用——判断数字正负
function isPos(n) {
  return (n === (n >>> 0)) ? true : false;  
}

isPos(-1); // false
isPos(1); // true

## 其他一些位运算的使用

网上翻的资料中还有很多利用位运算简化代码的例子,这里再罗列一些:


### 强制转成整数类型

由于做位运算时需要强制转换成整数类型,所以原本通过 parseInt 等方法得到值是 NaN 的类型,会在位运算时得到 0。

parseInt((function () {})) //NaN

(function () {}) | 0 //0
(function () {}) & 0 //0

### 转换字母大小写
//假设变量 char 是一个字母
char.charCodeAt(0) | 32 // 大写转小写
char.charCodeAt(0) & ~32  // 小写转大写

由于一些国家使用的字母(比如土耳其)无法通过 toLowerCasetoUpperCase 正确的转换字母大小写,所以使用位运算来实现该功能。

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;
};
AngularJS 中的源码

## 参考文献 - [js中位运算的运用](http://www.tuicool.com/articles/VJryYb) - [javascript的变态位运算](http://blog.sina.com.cn/s/blog_53d3c24a0100mpnv.html) - [小代码大学问之JavaScript位运算](http://www.tuicool.com/articles/yEJbyq)
## Thanks
@chechecarer
Copy link

向上取整,用~~似乎不对吧。可以用
var num = 3.14; var t = num >> 0; num = (num === t)? t ; t + 1;

@haocong
Copy link

haocong commented Sep 4, 2016

在非(~)运算那,Math.ceil(3.14) 不应该是等于4么?

@ranrantu
Copy link

@chechecarer 向上取整可以用 -3.14 把第一个替换成-即可。

@ricosmall
Copy link

向上取整可以 ~~3.14 + 1 或者(3.14 | 0) + 1

@ricosmall
Copy link

作者想说的应该是Math.floor

@hanbaoshashou
Copy link

(n & 1) === 0 //true n为偶数

@hellozhangran
Copy link

function isPos(n) {
  return (n === (n >>> 0)) ? true : false;  
}

isPos(-1); // false
isPos(1); // true

只能判断整数的正负,小数的就不灵了 ~

@Lie8466
Copy link

Lie8466 commented Nov 13, 2017

我在想《深入浅出 Node.js》“ Javascript 中只有 double 型的数据类型,在进行位运算的过程中,需要将 double 型转换为 int 型”这句话是不是有问题,应该是“ Javascript 中只有 int 型的数据类型,在进行位运算的过程中,需要将 double 型转换为 int 型”吧?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants