JavaScript 作为一门动态语言,提供了多种运算符,其中 位运算符(Bitwise Operators) 是一组较少被使用但非常强大的运算符。本文将深入介绍 ~(按位取反)运算符的作用、使用场景以及其在 JavaScript 开发中的实际应用。

一、~(按位取反)运算符简介

1. 什么是 ~ ?

~ 是 JavaScript 的 按位取反(Bitwise NOT) 运算符,它对数值的 每一位 进行翻转,即:

  • 0 变成 1
  • 1 变成 0

这个运算符的行为基于 32 位有符号整数 的二进制表示。它适用于 整数,但在 JavaScript 这种弱类型语言中,它也可以用于 隐式转换,甚至可以用于一些 特殊的技巧。

2. ~ 运算符的数学公式

在 JavaScript 中,~x 等价于:

~x = -(x + 1)

也就是说,~ 运算的结果是 负数,其值比原数字的相反数小 1。

示例:

console.log(~0);   // -1
console.log(~1);   // -2
console.log(~-1);  // 0
console.log(~5);   // -6

3. 为什么 ~x 等于 -(x + 1)?

让我们看一个 5 的二进制表示:

5  = 00000000 00000000 00000000 00000101  // 32 位二进制
~5 = 11111111 11111111 11111111 11111010  // 取反

在 JavaScript 的 二进制补码 规则下:

  • 最高位 1 表示负数
  • 负数的二进制存储方式是 补码

取反后,11111111 11111111 11111111 11111010 在 补码表示 下的十进制数就是 -6,因此 ~5 = -6。

二、~ 运算符的应用场景

虽然 ~ 不是最常见的运算符,但它在某些场景中可以带来 简洁 和 高效 的代码。下面介绍几个常见的应用场景。

1. 快速判断值是否存在于数组

在 JavaScript 中,我们通常使用 indexOf 来检查某个值是否在数组中:

const arr = [1, 2, 3, 4, 5];
if (arr.indexOf(3) !== -1) {
    console.log("3 存在于数组中");
}

但是可以使用 ~ 来简化:

if (~arr.indexOf(3)) {
    console.log("3 存在于数组中");
}
为什么 ~indexOf(x) 可以判断值是否存在?
  • indexOf(x) 返回 -1 时,表示找不到该元素
  • ~(-1) = 0,在 if (~indexOf(x)) 语句中,0 被视为 false
  • 如果 indexOf(x) 返回 非 -1 的索引,取反后仍然是一个 非零负数,在 if 判断中会被视为 true

示例:

console.log(~[-1, 0, 1].indexOf(1));  // -2 (true)
console.log(~[-1, 0, 1].indexOf(-1)); // 0  (false)
console.log(~[-1, 0, 1].indexOf(0));  // -2 (true)

这种技巧在 ES6 引入 includes 之前非常流行,但现在 includes 更可读:

if (arr.includes(3)) {
    console.log("3 存在于数组中");
}

2. 快速取反布尔值(仅适用于 0 和 -1)

由于 ~x = -(x + 1),它可以用于布尔值转换:

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

但 仅对 -1 有效,一般不建议这样使用,!! 更直观。

3. 用于二进制位操作

由于 ~ 直接作用于 二进制位,可以用于 掩码、反转标志位、快速计算补码 等操作:

const FLAGS = 0b10101010;  // 170
const INVERTED_FLAGS = ~FLAGS;
console.log(INVERTED_FLAGS.toString(2)); // 11111111111111111111111101010101

示例: 如果 FLAGS 代表某些开关(如 10101010),那么 ~FLAGS 可以用来得到所有 相反的标志位(即 01010101)。

三、注意事项

1. 仅适用于 32 位整数

JavaScript 采用 IEEE 754 双精度浮点数 进行运算,但 位运算符会将操作数转换为 32 位有符号整数:

console.log(~5.5); // -6
console.log(~(5.9)); // -6
console.log(~(-5.9)); // 4

所以 ~ 不适用于浮点数运算,它会 强制转换为整数。

2. 慎用 ~indexOf() 技巧

虽然 ~indexOf(x) 很流行,但它的可读性不如 includes():

if (arr.includes(3)) { // 更直观
    console.log("3 存在于数组中");
}

因此,在现代 JavaScript 代码中 推荐使用 includes()。

3. 负数取反变正数

由于 ~ 是对 补码 进行操作,所以对负数取反会 变成正数 -1:

console.log(~-1);  // 0
console.log(~-2);  // 1
console.log(~-3);  // 2

可以利用这个特性进行某些 数学计算优化。

四、总结

JavaScript 的 ~(按位取反)运算符虽然 不常用,但它具有一些 独特的用途,特别是在:

  1. 判断 indexOf 结果是否为 -1
  2. 二进制掩码、标志位处理
  3. 特定布尔值转换

尽管它在 ES6 之后的应用场景减少,但在某些 低级优化 和 位运算 场景下依然有一定价值。

推荐:


在这里插入图片描述

更多推荐