一个符号让 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


参考资料

相关推荐
Baklib梅梅21 小时前
优秀文档案例解析:打造高效用户体验的最佳实践
前端·ruby on rails·前端框架·ruby
慧一居士1 天前
VUE、jquery、React、Ant Design、element ui、bootstrap 前端框架的 功能总结,示例演示、使用场景介绍、完整对比总结
前端
GISer_Jing1 天前
0926第一个口头OC——快手主站前端
开发语言·前端·javascript
MediaTea1 天前
Jupyter Notebook:基于 Web 的交互式编程环境
前端·ide·人工智能·python·jupyter
少年阿闯~~1 天前
CSS——重排和重绘
前端
奶糖 肥晨1 天前
Uniapp 开发中遭遇「可选链赋值」语法陷阱:一次编译错误排查实录
javascript·vue.js·uni-app
个人看法1 天前
h5实现一个吸附在键盘上的工具栏
前端·javascript·vue
知识分享小能手1 天前
微信小程序入门学习教程,从入门到精通,微信小程序页面制作(2)
前端·javascript·学习·微信小程序·小程序·前端框架·notepad++
jason_yang1 天前
JavaScript 风格指南 精选版
前端·javascript·代码规范
小高0071 天前
🔍ECMAScript 2025 有哪些新特性?
前端·javascript