JavaScript 按位非运算符(~)原理与 indexOf 技巧解析
本文深入解析 JavaScript 中按位非运算符(~)的工作原理,展示如何利用其与 indexOf 方法的组合实现简洁的字符串存在性判断。通过对比 ~、>=0、Boolean 和 !! 四种写法在百万级循环中的执行耗时,探讨该技巧的实际性能表现与代码可读性权衡。
按位非运算符(~)基础原理
在 JavaScript 中,按位非运算符由一个波浪符号(~)表示,其核心功能是对数值执行按位取反操作,最终返回该数值的反码结果。
具体实现代码如下:
var num1 = 3; // 我的幸运数字是3
var num2 = ~(num1);
console.log(num2) // "-4"
var num3 = -3;
var num4 = ~(num3);
console.log(num4) // "2"
console.log(~(0)) // "-1"~与indexOf结合使用技巧
至此,我们明确了 ~ 运算符的基本原理。尽管这一知识点可能并不常用,甚至让部分开发者感到陌生,但理解其机制有助于掌握更底层的编程技巧。接下来探讨一个典型的应用场景:利用该运算符优化代码逻辑。参考以下同事编写的代码片段:
具体代码如下:
if (~item[search_key].toLowerCase().indexOf(query)) {
_results.push(item);
}上述代码展示了如何利用 ~ 运算符简化条件判断。通常,我们可能会使用以下常规写法:
具体代码如下:
if( str.indexOf(query) != -1 ) or if( str.indexOf(query) >= 0)接下来分析其背后的逻辑原理:
str.indexOf(query) 的返回值仅有两种情况:
1. 当字符串 str 包含子串 query 时,返回值为 0 或正整数。此时,对结果应用 ~ 运算符后,再通过 !! 转换为布尔值,即 !!~str.indexOf(query) 或 Boolean(~str.indexOf(query)),结果均为 true。
2. 当字符串 str 不包含子串 query 时,返回值为 -1。此时,~(-1) 的结果为 0,转换为布尔值后即为 false。
因此,在 indexOf 查询前添加 ~ 运算符,可以巧妙地将查找结果转换为布尔判断,使代码更加简洁。
性能测试与效率对比分析
最后,我们来评估一下这种写法的执行效率。直觉上,位运算通常比比较运算更快。以下是相关的性能测试代码:
具体代码如下:
var str = "hutaoer go go go!!!!! My lucky number is 33!!";
var query = 33;
var timeStart1 = new Date() - 0;
for(var i = 0; i < 100000000; i++) {
~str.indexOf(query)
}
var timeEnd1 = new Date() - 0;
console.log('~ cost time:' + (timeEnd1 - timeStart1));
// ~ cost time:9954 循环次数:10000000
// ~ cost time:104 循环次数: 100000
var timeStart2 = new Date() - 0;
for(var j = 0; j < 100000000; j++) {
str.indexOf(query) >= 0
}
var timeEnd2 = new Date() - 0;
console.log('>= cost time:' + (timeEnd2 - timeStart2));
// >= cost time:10120 循环次数:10000000程序更新说明:原测试代码保持不变,新增以下对比测试。具体代码如下:
具体代码如下:
var str = "hutaoer go go go!!!!! My lucky number is 33!!";
var query = 33;
var timeStart1 = new Date() - 0;
for(var i = 0; i < 1000000; i++) {
~str.indexOf(query)
}
var timeEnd1 = new Date() - 0;
console.log('~ cost time:' + (timeEnd1 - timeStart1));
// 循环1000000次 127ms
var timeStart2 = new Date() - 0;
for(var j = 0; j < 1000000; j++) {
str.indexOf(query) >= 0
}
var timeEnd2 = new Date() - 0;
console.log('>= cost time:' + (timeEnd2 - timeStart2));
// 循环1000000次 101ms
var timeStart3 = new Date() - 0;
for(var k = 0; k < 1000000; k++) {
Boolean(~str.indexOf(query))
}
var timeEnd3 = new Date() - 0;
console.log('add Boolean cost time:' + (timeEnd3 - timeStart3));
// 循环1000000次 129ms
var timeStart4 = new Date() - 0;
for(var k = 0; k < 1000000; k++) {
!!(~str.indexOf(query))
}
var timeEnd4 = new Date() - 0;
console.log('add !! cost time:' + (timeEnd4 - timeStart4));
// 循环10000000次 103ms
从测试结果可以看出,单次运算中各写法的耗时差异微乎其微。只有当循环次数极大(例如超过 10000000 次)时,不同写法之间的效率差距才会显现。
【更新 2013.10.27 17:28】根据修改后的测试数据,我们发现“按位非”这种写法并非效率最高,表现最佳的是传统的比较运算符写法。这一结果令人意外,也提醒我们常识或表象可能具有误导性,实际测试往往能揭示不同的结论。
在评论区,许多开发者对这种非常规写法持保留态度,认为它可能增加代码阅读难度,尤其是对于不熟悉其原理的维护者而言,可能造成理解障碍。相比之下,使用直观的逻辑判断和常见运算符可能是更稳妥的选择。
综上所述,日常编码中采用何种写法均可,但建议开发者掌握此类技巧,以便在特定场景下灵活应用。
JavaScript怎么准确判断数据类型:typeof、instanceof与Object.prototype.toString
2026-11-05 17:09:21
JS中将数组和对象混合组装为JSON字符串
2026-11-05 16:41:42
JS运算符优先级与结合性的区别是什么?
2026-11-05 13:22:02
JS Map怎么只遍历Key或Value
2026-11-05 13:00:36
D3.js 与 ECharts 选型对比:灵活性 vs 开发效率
2026-11-05 09:45:01
