在Javascript中,“double tilde”(即两个波浪线)有什么作用?

251
今天我正在查看一个在线游戏物理库,发现了~~操作符。我知道单个~是按位取反,那么~~是不是对一个非操作再做一次非操作,从而返回相同的值呢?

13个回答

287

因为位运算符隐式将操作数转换为带符号的32位整数,所以它会删除小数点后的所有内容。这适用于操作数是(浮点型)数字或字符串的情况,结果是一个数字。

换句话说,它产生以下结果:

function(x) {
  if(x < 0) return Math.ceil(x);
  else return Math.floor(x);
}

只有当x在-(231)和231-1之间时,才能使用此方法。否则,会发生溢出并且数字将"环绕"。

这可能有助于将函数的字符串参数转换为数字,但是由于可能会发生溢出并且它不能正确处理非整数,因此我不会以这种方式使用它,除非是用于"代码高尔夫"(即,在程序源代码的可读性和鲁棒性的代价下无意义地削减程序字节数)。相反,我会使用+xNumber(x)


如何求一个数的补码

例如,数字-43.2:

-43.210=111111111111111111111111110101012

作为带符号(二进制补码)的32位二进制数。(JavaScript忽略小数点后面的部分。)反转该位得到:

NOT -4310 = 000000000000000000000000001010102 = 4210

再次反转给出:

NOT 4210 = 111111111111111111111111110101012 = -4310

这与Math.floor(-43.2)不同,因为负数向零舍入,而不是远离它。(floor函数将等于-44,无论数字是正数还是负数,都会向下舍入到下一个较低的整数。)


9
换句话说,~~是一种简便的方式(可能也是一个不错的解决方案?)来创建截断函数,但显然只适用于javascript - ruffin
6
JSLint会抱怨使用~~ - Richard Cook
3
尝试使用Math.trunc()函数。 - Xitalogy

36
第一个 ~ 运算符将操作数强制转换为整数(可能在将值强制转换为字符串或布尔值后),然后反转最低的31位。官方规定 ECMAScript 的数字都是浮点数,但某些数字在 SpiderMonkey 引擎中被实现为31位整数。
您可以使用它将一个1元素数组转换为整数。浮点数按照C语言的规则进行转换,即截断小数部分。
第二个 ~ 运算符然后反转回来,因此您知道将得到一个整数。这与在条件语句中将值强制转换为布尔值不同,因为空对象 {} 计算结果为 true,而 ~~{} 的计算结果为 false。
js>~~"yes"
0
js>~~3
3
js>~~"yes"
0
js>~~false
0
js>~~""
0
js>~~true
1
js>~~"3"
3
js>~~{}
0
js>~~{a:2}
0
js>~~[2]
2
js>~~[2,3]
0
js>~~{toString: function() {return 4}}
4
js>~~NaN
0
js>~~[4.5]
4
js>~~5.6
5
js>~~-5.6
-5

1
感谢您提供的所有示例,Shanti,这确实帮了我很多! - Shane Tomlinson
9
同样的 ~~undefined // 0 可以翻译为:~~未定义 // 0 - rampion
2
也就是 ~~null // 0 - chovy
从技术上讲,您的顺序是错误的。第二个~执行了您描述的第一个~的操作,反之亦然。~运算符是一元运算符,从右到左解释。~~X就像~(~X)而不是(~~)X(这将是语法错误)。 - yunzen

25
在ECMAScript 6中,等同于~~的是Math.trunc

通过删除任何小数位来返回数字的整数部分。它不会将任何数字四舍五入。

Math.trunc(13.37)   // 13
Math.trunc(42.84)   // 42
Math.trunc(0.123)   //  0
Math.trunc(-0.123)  // -0
Math.trunc("-1.123")// -1
Math.trunc(NaN)     // NaN
Math.trunc("foo")   // NaN
Math.trunc()        // NaN

填充物(polyfill):

function trunc(x) {
    return x < 0 ? Math.ceil(x) : Math.floor(x);
}

7
有些令人惊讶的是,~~ 操作符比 Math.trunc 更快,http://jsperf.com/math-trunc-vs-double-bitwise-not-operator。不过,并非一切都只关乎速度,也要考虑可读性。 - Gajus
3
和Math.trunc之间有一个重要的区别:如果你传递一个字符串、NaN或任何不是数字的东西,Math.trunc将返回NaN,而将始终返回一个数字,在这些情况下它将返回0。 - Buzinas
根据http://jsperf.com/math-trunc-vs-double-bitwise-not-operator的测试结果,在Chrome 59+中,Math.trunc比~~略微更快。 - Jack Steam

14

感谢您提供的链接,Drackir! - Shane Tomlinson

11

假设~N等于-(N+1),则~~N等于-(-(N+1) + 1)。显然,这导致了一个妙招。


必须向下滚动到Matt的评论才能看到它的正确使用 ;) - mplungjan
那...让我大开眼界。 - Raydot

9

提醒一下,这里的其他答案给我带来了一些麻烦。

本意是删除浮点数小数点后面的所有内容,但它有一些特殊情况,可能会导致错误。建议避免使用~~。

首先,~~无法处理非常大的数字。

~~1000000000000 == -727279968

作为替代方案,可以使用Math.trunc()(正如Gajus所提到的,Math.trunc()返回浮点数的整数部分,但仅适用于符合ECMAScript 6标准的JavaScript)。您可以通过执行以下操作在非ECMAScript-6环境中创建自己的Math.trunc()

if(!Math.trunc){
    Math.trunc = function(value){
        return Math.sign(value) * Math.floor(Math.abs(value));
    }
}

我写了一篇博客文章供参考:http://bitlords.blogspot.com/2016/08/the-double-tilde-x-technique-in.html

此处是关于 IT 技术的内容,介绍了双波浪线 X 技术。请参考上述链接了解详情。

5

字符串转换为数字

console.log(~~-1);    // -1
console.log(~~0);     // 0
console.log(~~1);     // 1
console.log(~~"-1");  // -1
console.log(~~"0");   // 0
console.log(~~"1");   // 1
console.log(~~true);  // 1
console.log(~~false); // 0

~-1等于0

if (~someStr.indexOf("a")) {
  // Found it
} else  {
  // Not Found
}

source


2

~~可以被用作Math.trunc()的简写。

~~8.29 // 输出 8

Math.trunc(8.29) // 输出 8


1
"Tilde(~)"有一个算法-(N+1)。
例如:
~0 = -(0+1) = -1
~5 = -(5+1) = -6
~-7 = -(-7+1) = 6

双波浪线为-(-(N+1)+1)

例如:

~~5 = -(-(5+1)+1) = 5
~~-3 = -(-(-3+1)+1) = -3

三重波浪线是-(-(-(N+1)+1)+1)

例如:

~~~2 = -(-(-(2+1)+1)+1) = -3
~~~3 = -(-(-(3+1)+1)+1) = -4

1
这里有一个示例,展示了如何高效地使用该运算符,在某些情况下使用它是有意义的:
leftOffset = -(~~$('html').css('padding-left').replace('px', '') + ~~$('body').css('margin-left').replace('px', '')),

源代码:

查看与点交互部分


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