今天在项目中看到有这样一段代码:

if(~key.indexOf('I')){
    priceTagData.adulti = {
        tag: key,
        price: value
    }
}

我表示对~这个操作符感到无比陌生,自然也就理解不了作者的意图。由于目前本人的求知欲正处于旺盛期,所以决定学习一下这个操作符的用处。

看了眼ES5,原来它是位取反操作符。下面是ES5对这个操作符的说明:

Bitwise NOT Operator ( ~ )

The production UnaryExpression:~UnaryExpression is evaluated as follows:

  1. Let expr be the result of evaluating UnaryExpression.

  2. Let oldValue be ToInt32(GetValue(expr)).

  3. Return the result of applying bitwise complement to oldValue. The result is a signed 32-bit integer.

大概求值过程就是:

1.先求值~右边的表达式,设求值结果为expr

2.执行ToInt32(expr),设返回结果为oldValue

因为ECMAScript中Number类型存储格式是64位浮点数,所以在进行位操作前要把浮点数转换为32位有符号整数,就是上边说明中的ToInt32方法。ToInt32规范定义的内部类型转换方法,ECMAScript程序使用者不能直接用,ToInt32具体说明点这里

3. 将oldValue按位取反并返回

由于返回的是32位整数,所以还需要将这个整数转换为64位浮点数。由此可见,ECMAScript中的位操作并不像c,c++那样直接操作位,它需要在位操作之前和之后进行浮点数转成整数和整数转成浮点数的操作。


看一个具体例子:~4294962318,我们通过上边规范的定义来意淫下结果。。。

首先求值~右边的表达式,求值后就是4294962318

然后执行ToInt32(4294962318),

ToInt32的内部执行过程为:

4294962318进行ToNumber转换,结果依旧为4294962318

求值sign(4294962318) *floor(abs(4294962318)),结果依旧为4294962318

求值4294962318 modulo 4294967296(2^32)的值,结果为4294962318

4294962318 大于2147483648(2^31),所以返回4294962318 - 4294967296,也就是-4978

sign , floor , abs , modulo 用法参见-> 这里

接下来要把-4978转换成2进制,为了方便我写了段程序把一个10进制转换成2进制,下边是c++源码,程序见附件(下载后为文件添加.exe后缀),你可以通过这个程序转换你想转换的数字

#include <iostream>
using namespace std;
int main () {
 int t;
 cout << "输入一个要转换为2进制表示形式的数字:";
 cin >> t;
 char str[255];
 itoa(t, str, 2);
 cout << str << endl;
 system("pause");
 return 0;
}

-4978用二进制表示为  1111 1111 1111 1111 1110 1100 1000 1110

按位取反后就是           0000 0000 0000 0000 0001 0011 0111 0001

这个取反后的结果理论上就是~4294962318计算出的值

接下来我们在js控制台执行~4294962318,得到4977

再用程序将4977转换成二进制结果是0000 0000 0000 0000 0001 0011 0111 0001

你会发现和我们推导出的二进制是一样的


理论和实践都讲过之后,我们再回到最初的问题:

if(~key.indexOf('I')) {

作者这句到底想要干什么。。。

indexOf大家应该都知道,它是字符串的一个方法,用来查找传入的字符在字符串中第一次出现的位置,如果有,则返回该字符在字符串中的索引值,如果没有返回-1。

~-1正好等于0,而0在ECMAScript if条件中又相当于false,非0数都相当于true。终于!!!我明白了作者的意图,那就是如果key字符串中如果含有I那就执行if条件为true中的语句,如果不含有I,那就不执行if条件为true中的语句。

和这种高帅富写法等效的屌丝写法就是:

if(key.indexOf('I') !== -1){
    saveTheEarth();
}

最后就是吐槽时间了。。。

我个人不是很赞成这种怪异的写法(相对于我)。因为jser在平时编程中需要使用位操作的地方还算比较少的,所以在多人项目中如果写了这样一句代码,日后别人来维护这块代码时就会感到一头雾水,无形中增加了项目维护的难度和成本。