jstips 技巧 15:用 `~` 位运算把 `indexOf` 变成优雅的 contains 判断
发布时间:2026/10/8 6:39:28 锦皓数字建站

教程【免费下载链接】jstipsThis is about useful JS tips!项目地址https://gitcode.com/gh_mirrors/js/jstips点击查看免费下载JavaScript 原生没有提供contains方法日常判断字符串是否包含某个子串或数组是否包含某个元素时最常见的写法是someText.indexOf(javascript) ! -1。本文基于 jstips 仓库 的 Tip 15原文讲解如何用位运算~将indexOf的返回值直接当作布尔值使用并对比 ES6/ES7 引入的includes()方法让你在字符串与数组的包含性判断上写出更简洁、可读的代码同时清楚每种写法的适用前提与兼容性边界。常规做法indexOf 显式比较String.prototype.indexOf()与Array.prototype.indexOf()都会在找不到时返回-1找到时返回首次出现的索引非负整数。因此最常见的 contains 判断是var someText javascript rules; if (someText.indexOf(javascript) ! -1) { // someText 包含 javascript } // 等价写法 if (someText.indexOf(javascript) 0) { // someText 包含 javascript }两种写法完全等价只是! -1语义上更贴近存在性判断而 0强调索引合法。它们都正确但略显啰嗦——你真正关心的只是一个布尔结果却不得不和一个魔法数字-1做比较。更简洁的写法利用位运算~Express 源码中大量使用了一个更简洁的技巧。以 examples/mvc/lib/boot.js 中的片段为例for (var key in obj) { // reserved exports if (~[name, prefix, engine, before].indexOf(key)) continue;这里用~按位取反bitwise NOT包裹indexOf的返回值直接在if条件里充当布尔值。同样lib/utils.js 用它判断 MIME 类型是否包含/exports.normalizeType function(type){ return ~type.indexOf(/) ? acceptParams(type) : { value: mime.lookup(type), params: {} }; };而在 examples/web-service/index.js 中则用!~表达不包含// key is invalid if (!~apiKeys.indexOf(key)) return next(error(401, invalid api key));为什么~能这样用位运算~对数字按二进制位取反运算过程基于 32 位二进制表示但返回的是标准 JavaScript 数值。关键规律是~(-1) 0其它非负整数 n 的~n均为负数例如~0 -1、~1 -2、~2 -3由于indexOf找不到时返回-1找到时返回 0的索引因此~indexOf(...)为0⇔ 索引为-1⇔不包含而0在if条件中为 falsy~indexOf(...)为非 0 ⇔ 索引 0⇔包含非 0 值在if条件中为 truthy。也就是说~把indexOf的返回值归一化成了可直接用于条件判断的真值省掉了与-1的比较。验证一下var someText text; !!~someText.indexOf(tex); // someText contains tex - true !~someText.indexOf(tex); // someText NOT contains tex - false ~someText.indexOf(asd); // someText doesnt contain asd - false ~someText.indexOf(ext); // someText contains ext - true配合双重取反!!可以把它严格规范为布尔值true/false配合!~则直接表达不包含。需要注意的边界~是一个通用位运算它的行为并不只服务于indexOf使用时务必留意两点可参见同仓库 Tip 18双波浪号截断 对位运算的深入剖析只对找不到返回 -1的 API 成立indexOf的返回值恰好是-1或 0这是该技巧能成立的前提。若某个 API 在失败时返回null、undefined或其他值~的结果会不可预测。32 位整数限制位运算会把操作数先转成 32 位有符号整数。对索引值而言通常无碍但若数值超出 32 位范围约 ±21.47 亿结果会失真。日常的字符串/数组索引场景基本不会触发但不要把它推广到任意大数的位运算上。更现代的替代方案includes()ES6 为字符串引入了String.prototype.includes()可以直接判断一个字符串是否包含另一个字符串something.includes(thing); // trueECMAScript 2016ES7又把includes()扩展到了数组用法与indexOf风格一致!!~[1, 2, 3].indexOf(1); // true [1, 2, 3].includes(1); // true相比之下includes()语义直白、可读性最好是团队协作与长期维护场景下的首选。仓库内其他 Tip 也大量使用了includes()处理剔除某些键之类的判断例如 Tip 70picking and rejecting object properties 中的Object.keys(obj).filter(k !keys.includes(k))可作为实际工程中的参考用法。兼容性前提注意includes()仅被 Chrome、Firefox、Safari 9 及以上版本和 Edge 支持IE11 及更低版本不支持它更适合在可控的运行环境中使用。若需要兼容旧环境退回到indexOf系列的写法即可。另外Array.prototype.includes()与indexOf在判断NaN上行为不同[NaN].indexOf(NaN)返回-1而[NaN].includes(NaN)返回true两者对0/-0的判定也有细微差别需要精确判断元素时要注意。小结写法示例特点indexOf! -1s.indexOf(x) ! -1语义清晰兼容所有环境位运算~~s.indexOf(x)/!~s.indexOf(x)最简洁老代码中常见但依赖 32 位位运算语义String/Array.includes()s.includes(x)/arr.includes(x)语义最佳ES6/ES7 特性需注意 IE11 不支持三种方式都能完成包含性判断。作为工程建议新代码优先使用includes()在必须兼容旧浏览器或需要强调存在索引的场景下使用indexOf ! -1而~技巧适合在受控环境、追求极简表达例如配合!~表示不包含并提前return/continue时使用同时牢记其位运算边界。赞分享教程【免费下载链接】jstipsThis is about useful JS tips!项目地址https://gitcode.com/gh_mirrors/js/jstips点击查看免费下载相关推荐WPScan 动态版本检测实战以 Grid Plus 插件 CHANGELOG.md 为例解析 ChangeLog 版本指纹机制WPScan 动态版本检测实战以 Grid Plus 插件 CHANGELOG.md 为例解析 ChangeLog 版本指纹机制 WPScan 作为面向 Wo教程终极bash条件判断简写指南与||运算符应用技巧终极bash条件判断简写指南与||运算符应用技巧 想要快速掌握bash脚本中的条件判断技巧吗作为Linux和Unix系统中最重要的shell编程语言b教程剑指 Offer 15 二进制中 1 的个数逐位判断与 n(n-1) 位运算解法详解Python/Java/C剑指 Offer 15 二进制中 1 的个数逐位判断与 n n 1 位运算解法详解Python/Java/C 本篇基于 LeetCode Book示例工程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。