一个符号让 indexOf 判断更优雅!JavaScript 位运算的隐藏技巧

前言

在日常开发中,你是否经常写这样的代码?

js 复制代码
const index = source.indexOf(target);
if (index !== -1) {
  // 找到了,执行逻辑
} else {
  // 没找到,执行逻辑
}

今天我要分享一个只需要一个符号就能让这段代码变得更优雅的技巧!不仅如此,我们还会深入探讨其背后的计算机原理,让你真正理解位运算的魅力。

问题场景

假设我们需要判断一个元素是否存在于数组中,这是前端开发中最常见的场景之一。

传统写法:

js 复制代码
const arr = ['apple', 'banana', 'orange'];
const index = arr.indexOf('apple');

if (index !== -1) {
  console.log('找到了 apple');
} else {
  console.log('没找到 apple');
}

这种写法虽然清晰,但每次都要写 !== -1 是不是有点繁琐?

优雅的解决方案

使用位运算的取反操作(~),我们可以将代码简化为:

js 复制代码
const arr = ['apple', 'banana', 'orange'];

if (~arr.indexOf('apple')) {
  console.log('找到了 apple');
} else {
  console.log('没找到 apple');
}

仅仅一个 ~ 符号就完成了判断!

深入理解位运算 ~

JavaScript 数字的存储方式

首先,我们需要了解 JavaScript 中数字的存储方式:

JavaScript 采用"IEEE 754 标准定义的双精度64位格式"表示数字。

虽然 JavaScript 使用 64 位浮点数存储数字,但在进行位运算时,会转换为 32 位有符号整数进行计算。

位运算 ~ 的工作原理

位运算 ~ 的作用是按位取反,包括符号位。让我们通过一个例子来理解:

问题:~0 等于多少?

让我们逐步分析:

  1. 转换为 32 位二进制:

    js 复制代码
    0 => 0000 0000 0000 0000 0000 0000 0000 0000
  2. 按位取反:

    js 复制代码
    ~(0000 0000 0000 0000 0000 0000 0000 0000) 
    > 1111 1111 1111 1111 1111 1111 1111 1111
  3. 解析结果 : 由于最高位(第31位)为 1,表示这是一个负数。要得到这个负数的值,我们需要进行补码的逆运算。

补码的逆运算

补码的逆运算过程:

  1. 减 1:

    js 复制代码
      1111 1111 1111 1111 1111 1111 1111 1111
    -                                       1
    = 1111 1111 1111 1111 1111 1111 1111 1110
  2. 按位取反:

    js 复制代码
    ~(1111 1111 1111 1111 1111 1111 1111 1110)
    > 0000 0000 0000 0000 0000 0000 0000 0001 (=1)
  3. 添加负号:-1

因此,~0 = -1。

通用规律

通过大量测试,我们可以总结出一个规律: 一个数的取反操作结果等于 -(当前数 + 1)

例如:

  • ~0 = -1
  • ~1 = -2
  • ~(-1) = 0
  • ~(-2) = 1

补码的计算机原理

为了更好地理解位运算,我们需要了解计算机中负数的表示方法------补码。

什么是补码?

补码是一种数值转换方法,分为两步:

  1. 按位取反:将每一位都取反(0 变 1,1 变 0);
  2. 加 1:将取反后的结果加 1。

示例:求 -8 的补码表示

js 复制代码
原数 0000 1000 (8)
取反 1111 0111
加一 1111 0111 + 1 = 1111 1000
结果 1111 1000 就是 -8 的补码表示

为什么使用补码?

计算机使用补码表示负数有以下几个优势:

  1. 简化运算:减法可以转换为加法运算
  2. 统一表示:正数、负数和 0 都有唯一的表示
  3. 扩展范围:8 位补码可以表示 -128, 127,比原码和反码多表示一个数

补码的本质

补码的本质是:负数 = 0 - 正数

以 -8 为例:

js 复制代码
   0 0 0 0 0 0 0 0  (0)
-  0 0 0 0 1 0 0 0  (8)
=  1 1 1 1 1 0 0 0  (-8)

由于被减数小于减数,需要借位,实际上相当于:

js 复制代码
   1 0 0 0 0 0 0 0 0  (借位后的被减数)
-    0 0 0 0 1 0 0 0  (减数)
=  1 1 1 1 1 0 0 0 0  (结果)

进一步分解:

js 复制代码
> 1 0 0 0 0 0 0 0 0 = 1 1 1 1 1 1 1 1 + 0 0 0 0 0 0 0 1

So:
   1 1 1 1 1 1 1 1
-  0 0 0 0 1 0 0 0
=  1 1 1 1 0 1 1 1
+  0 0 0 0 0 0 0 1
=  1 1 1 1 1 0 0 0

这就是补码转换步骤的由来。

回到业务场景

现在我们可以完美解释为什么 ~source.indexOf(target) 能够优雅地判断元素是否存在:

分析过程

  1. indexOf 返回 -1(元素不存在):

    • ~(-1) = 0
    • 0 在布尔上下文中为 false
  2. indexOf 返回其他值(元素存在):

    • ~0 = -1,~1 = -2,~2 = -3 等
    • 非零值在布尔上下文中为 true

代码示例

js 复制代码
const arr = ['apple', 'banana', 'orange'];

// 传统写法
if (arr.indexOf('apple') !== -1) {
  console.log('找到了 apple');
}

// 优雅写法
if (~arr.indexOf('apple')) {
  console.log('找到了 apple');
}

// 更多示例
console.log(~arr.indexOf('grape'));  // 0 (false)
console.log(~arr.indexOf('apple'));  // -1 (true)
console.log(~arr.indexOf('banana')); // -2 (true)

性能考虑

虽然 ~ 操作看起来更简洁,但在实际项目中需要考虑:

  1. 可读性:对于不熟悉位运算的开发者,可能影响代码可读性
  2. 性能:位运算通常比普通比较运算更快
  3. 兼容性:现代浏览器都支持位运算

其他位运算技巧

除了 ~ 操作,JavaScript 中还有其他有用的位运算技巧:

js 复制代码
// 快速判断奇偶
const isEven = n => !(n & 1);

// 快速取整
const floor = n => n | 0;

// 快速判断是否为2的幂
const isPowerOf2 = n => n > 0 && !(n & (n - 1));

// 快速交换两个数(不使用临时变量)
let a = 5, b = 3;
a ^= b; b ^= a; a ^= b;
console.log(a, b); // 3, 5

总结

通过深入理解补码原理和位运算机制,我们不仅学会了如何优雅地使用 ~ 操作符简化 indexOf 的判断逻辑,更重要的是理解了计算机底层的数据表示方式。

这种从实际问题出发,深入原理的学习方式,能够帮助我们更好地掌握编程技能,写出更优雅、更高效的代码。

记住这个公式 :~n = -(n + 1)

记住这个技巧 :~indexOf() 替代 !== -1


参考资料

相关推荐
福兮说9 小时前
设计稿是 #4A7C6F,页面量出来是 #4B7C6F:HEX、HSL、透明度、canvas 来回转的七个坑
前端·javascript·css·canvas
郑州光合科技余经理9 小时前
海外版外卖加盟:总站与分站配送规则怎么分开管
java·开发语言·前端·后端·uni-app·php·ai编程
小呆呆66610 小时前
副业搞起来,小说,漫画,漫剧的成本优化思路
前端·后端·面试
凤城老人10 小时前
从 PyQt6 到 Electron:给 Edge TTS 做一个“多角色配音机“的踩坑手记
javascript·typescript·electron
Dovis(誓平步青云)10 小时前
浇水提醒刚弹出又消失,植物状态别只存一个百分比
开发语言·前端·javascript·pdf·ecmascript·电脑
码艺-Alimjan11 小时前
Vben Admin 新增维吾尔语 Vben-Modal的关键坑之一
前端·javascript·vue.js
可乐鸡翅yeah_11 小时前
hls.js 手动自定义 http 请求 loader,修改请求头实战
开发语言·前端·javascript·网络协议·http·ecmascript·m3u8在线
IT_陈寒11 小时前
Vite静态资源导入这个坑我帮你们踩过了
前端·人工智能·后端
广州华水科技12 小时前
大坝安全监测解决方案:单北斗GNSS形变监测系统应用与维护
前端
yivifu12 小时前
中文古籍电子书注释集成
前端·javascript·python·beautifulsoup·epub