使用 !!~(非非波浪号)如何改变“包含/包括”数组方法调用的结果?

99
如果您阅读jQuery inArray页面这里的评论,会发现一个有趣的声明:
!!~jQuery.inArray(elm, arr) 

我相信两个感叹号会将结果转换为boolean类型,并具有 true的值。但我不明白在这一过程中波浪号 (~) 运算符的作用是什么?

var arr = ["one", "two", "three"];
if (jQuery.inArray("one", arr) > -1) { alert("Found"); }

重构 if 语句:

if (!!~jQuery.inArray("one", arr)) { alert("Found"); }

细分:

jQuery.inArray("one", arr)     // 0
~jQuery.inArray("one", arr)    // -1 (why?)
!~jQuery.inArray("one", arr)   // false
!!~jQuery.inArray("one", arr)  // true

我还注意到,如果在前面加上波浪线,则结果为-2

~!!~jQuery.inArray("one", arr) // -2

我不明白这里波浪线的用途。能否有人解释一下,或者指向相关资源?


52
那些写出这样代码的人需要暂停敲键盘。 - Kirk Woll
12
为什么?~jQuery.inArray()实际上非常有用,可能甚至是搜索函数返回失败时为什么返回-1(唯一一个二进制补码为false的值)的非常好的原因。一旦你看到并理解了这个技巧,我认为它比!= -1更易读。 - Amadan
9
@Amadan -- 不行。真的不行。我无法相信你会为了任何事情而辩护 !!~ - Kirk Woll
24
问题在于,这只是一个“技巧”。对我而言,if (x != -1)if (~x)的主要区别在于前者实际上表达了你打算做什么,而后者表达的是你想要完全做另一件事情(“请将我的64位数字转换成32位整数,并检查该整数的按位取反是否为真”),只是恰巧在这种情况下获得了所需的结果。 - JimmiTh
10
“>= 0” 可能不够“1337(指黑客语言)”,所以选择了更加神秘的“!!~”。 - Yoshi
显示剩余13条评论
13个回答

125

有时候你会看到在$.inArray前面加上~,这是有特定原因的。

基本上,

~$.inArray("foo", bar)

这是一种更简洁的做法。

$.inArray("foo", bar) !== -1
$.inArray 返回数组中第一个参数所在的位置索引,如果没找到则返回-1。这意味着如果你想要检查某个值是否在数组中,你不能使用布尔比较,因为-1是一个真值(truthy value),而当 $.inArray 返回 0(一个假值)时,实际上是找到了该值在第一个元素中。
应用位非(bitwise NOT)操作符~会导致 -1 变成0,而0变成-1。因此,在数组中找不到该值并应用位非运算将产生一个假值(0),而所有其他值将返回非0数字,并表示一个真值结果。
if (~$.inArray("foo", ["foo",2,3])) {
    // Will run
}

它会按预期工作。


2
这在浏览器中得到了多好的支持(现在是2014年)?还是一直完美支持? - Explosion Pills
如果这样基本的操作不完美,我会感到惊讶。 - pcarvalho

107

!!~expr 的结果在 expr-1 时为 false,否则为 true。它和 expr != -1 是等价的,只是使用了一种更加难懂的方式。


这段代码可以工作是因为 JavaScript 位运算符 将操作数转换成二进制的32位有符号整数进行计算。因此,!!~-1 的计算过程如下:

   -1 = 1111 1111 1111 1111 1111 1111 1111 1111b // two's complement representation of -1
  ~-1 = 0000 0000 0000 0000 0000 0000 0000 0000b // ~ is bitwise not (invert all bits)
   !0 = true                                     // ! is logical not (true for falsy)
!true = false                                    // duh

一个不等于-1的值至少有一位为零;取反将创建一个真值;对真值应用两次!运算符将返回布尔值true。

当与.indexOf()一起使用时,我们只想检查结果是否为-1

!!~"abc".indexOf("d") // indexOf() returns -1, the expression evaluates to false
!!~"abc".indexOf("a") // indexOf() returns  0, the expression evaluates to true
!!~"abc".indexOf("b") // indexOf() returns  1, the expression evaluates to true
* !!~8589934591 的结果为false,因此不能可靠地用来测试-1

1
在一个稳定的库中,我认为使用~foo.indexOf(bar)没有任何问题,虽然它并不能显著地节省字符或提高性能,但这是一种相对常见的简写方式,就像foo = foo || {}一样。 - zzzzBov
6
这不是问题...至少在有人被要求继续你的代码之前不是。 - Salman A
9
@zzzzBov 希望进一步阐述 Salman 的评论:以编写代码的方式,就好像最终维护你的代码的人是一个狂暴的精神病患者,他知道你住在哪里。这个观点来源于 Jeff Atwood 的博客文章《为狂暴的精神病患者编程》。 - ahsteele
1
@ahsteele,我非常清楚这个规则,但是位运算符是我所知道的每种编程语言的一部分。 我尽量以一种对能读懂代码的人来说可读的方式编程。 我不会仅因为别人不理解而停止使用语言的功能,否则我甚至无法使用!!操作符 (https://dev59.com/jXRA5IYBdhLWcg3w6SRH)。 - zzzzBov
如果你从Access数据库中读取数据,这个工具看起来非常方便,因为“false”被编码为-1,“true”被编码为0。 - CodeManX
显示剩余2条评论

58

波浪符操作符实际上并不是jQuery的一部分——它是JavaScript本身的按位非运算符。

请参见《波浪符(~)的伟大谜团》

你在实验中得到奇怪的数字是因为你对一个整数执行了按位逻辑运算(据我所知,这个整数可能以二进制补码等形式存储)。

《二进制补码》解释了如何用二进制表示一个数字。我认为我是正确的。


3
已更正!(将其更改为另一个链接,很奇怪的是,它是在我最初的回答之后编写的...) - p.g.l.hall

35

~foo.indexOf(bar) 是一种常见的简写方式,表示 foo.contains(bar),因为contains函数在JavaScript中不存在。

通常情况下,由于JavaScript的“falsy”值概念,将其转换为布尔值是不必要的。但在这种情况下,它被用来强制该函数的输出为 truefalse


7
这个回答比被采纳的回答更好地解释了“为什么”的原因。 - nalply

18

jQuery.inArray() 返回值为-1 表示“未找到”,其补码(~)为0。因此,~jQuery.inArray() 对于“未找到”返回一个falsy值(0),对于“已找到”返回一个truthy值(负整数)。接着,!! 将falsy/truthy形式化为真正的布尔类型 false/true。所以,!!~jQuery.inArray() 会对于“已找到”返回true,对于“未找到”返回false


13

所有4字节的int的取反符号~等于这个公式-(N+1)

SO

~0   = -(0+1)   // -1
~35  = -(35+1)  // -36 
~-35 = -(-35+1) //34 

3
这并不总是正确的,例如 ~2147483648 != -(2147483648 + 1) - Frxstrem

11

按位取反运算是指对一个值的每一位进行反转。通常情况下,如果你对一个数使用 ~ 运算符,它的符号会被反转,然后再减去 1。

因此,当你对 ~0 进行运算时,会得到 -1(0被反转为-0,减去1变成-1)。

它本质上是一种复杂的、超微观优化的方式,用于获取一个始终是布尔类型的值。


10

~运算符是按位取反运算符。inArray()函数返回的整数结果要么是非负整数,要么是-1表示未找到元素。-1的按位取反(在二进制中为全1)得到的是0。任何非负整数的按位取反结果总是非零。

因此,当整数"i"是非负整数时,!!~i的值为true,当"i"恰好为-1时,!!~i的值为false

请注意,~运算符始终将其操作数强制转换为整数;即,它将非整数浮点值以及非数字值强制转换为整数。


8

您说得对:当indexOf调用返回-1时,此代码将返回false;否则返回true

像您所说的那样,使用类似以下代码会更加明智:

return this.modifiedPaths.indexOf(path) !== -1;

1
但这会多发送3个字节给客户端!编辑:(顺便说一下,我开玩笑的,发表了我的评论后意识到这不是很明显(这既悲哀又傻)) - Wesley Murch
@Wesley:没错,但只需要将它发送给每个客户端一次,假设客户端将缓存.js。话虽如此,他们可以使用>=0而不是!==-1——没有额外的字节要发送,仍然比位操作版本更易读。 - LukeH
2
谁在这里恶作剧?;) 我想我认为编写可读代码比生成这些问题的加密预优化代码更好。稍后进行缩小并现在编写可读,易懂的代码。 - Wesley Murch
2
个人而言,我认为 > -1 更易读,但这可能非常主观。 - Yoshi

6
~ 运算符是按位取反运算符。这意味着它将以二进制形式表示的数字的所有零变为一,所有一变为零。
例如,十进制数 0 在二进制下为 0000000,而 -1 在二进制下为 11111111。同样地,十进制数 1 在二进制下为 00000001,而 -2 在二进制下为 11111110

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接