JavaScript中,按位非(~)的介绍以及妙用
最近改老代码经常看到这个符号,对于一个新人,很少见这个符号,初看还是有点阅读阻碍的。所以就查了一下。
按位非(~)
按位非运算符(~
)将操作数的位反转。如同其他位运算符一样,它将操作数转化为 32 位的有符号整型。 简单理解就是 ~a
等价于-(a + 1)
。
示例:
javascript
let arr = [1,2,3,4];
let item = 5;
if (~arr.indexOf(item)) {
....
}
这段代码实际上是在利用按位非运算符的一个小技巧。
indexOf
方法会返回查找元素的索引,如果找到了元素,其返回值是 0
或正整数(表示元素的位置索引)。如果没有找到,它会返回 -1
。
按位非运算符 ~
的一个特性是,当应用于 -1
时,它会返回 0
(因为 -1
在二进制中是所有位都为 1
,取反后所有位变为 0
),而当应用于任何其他的非负整数时,结果总是一个负值。
这意味着你可以这样用它来检查 indexOf
的结果:
javascript
if (~arr.indexOf(item)) {
// 如果 item 存在于数组中,indexOf 返回非 -1 的索引,取反后为负值,
// 由于负值在 if 条件判断中为真,这个代码块会执行。
} else {
// 如果 item 不存在于数组中,indexOf 返回 -1,取反后为 0,
// 0 在 if 条件判断中为假,这个代码块不会执行。
}
这是一种在 JavaScript 中比较老练的方式来检查一个元素是否存在于数组中。然而,这种方法的可读性并不是很好,特别是对于不熟悉这个技巧的开发者。更现代、更直观的方法是使用 includes
方法,它返回一个布尔值,直接告诉你元素是否存在于数组中:
javascript
if (arr.includes(item)) {
// 如果 item 存在于数组中,执行这个代码块
} else {
// 如果 item 不存在于数组中,执行这个代码块
}
这种方式更容易读懂,也是推荐的做法。说到底这种方式就不太适合阅读,然后有天看到了篇更加逆天的文章。
双按位非 (~~)
双重位运算符 NOT (~~) 在 JavaScript 中的作用是将一个数值取反,然后再次取反。具体来说,它会将操作数转换为32位整数,然后将每个位取反,即将0变为1,将1变为0。这个操作等同于 -(foo + 1)。因此,双重位运算符 NOT (~~foo) 的结果将是 -(-(foo + 1) + 1)。
然而,这个操作仅对整数有效。对于所有可能的操作数,~~ 的实际等效表达式可能是这样的:
javascript
typeof foo === 'number' && !isNaN(foo) && foo !== Infinity
? foo > 0 ? Math.floor(foo) : Math.ceil(foo) : 0;
这意味着,如果操作数是数字且不是 NaN 或 Infinity,那么 ~~ 的效果将向零取整(负数向上取整,正数向下取整)。如果操作数不是数字,那么内部 ToInt32 函数将其转换为零。
以下是一些双重位运算符 NOT (~~) 的示例:
javascript
~~null; // => 0
~~undefined; // => 0
~~0; // => 0
~~{}; // => 0
~~[]; // => 0
~~(1/0); // => 0
~~false; // => 0
~~true; // => 1
~~1.2543; // => 1
~~4.9; // => 4
~~(-2.999); // => -2
这种方法对于规范化预期为整数的参数也很有用。例如,MDC 建议为不支持的浏览器提供 Array.prototype.indexOf 时可以使用这种方法:
javascript
/*Array.prototype.indexOf = function...*/
var from = Number(arguments[1]) || 0;
from = (from < 0)
? Math.ceil(from)
: Math.floor(from);
使用双重位运算符 NOT (~~) 后:
javascript
/*Array.prototype.indexOf = function...*/
var from = ~~arguments[1];
看完之后,真是震惊。所以,多用用这种不常用的方式,是不是也算防御式编程
。