JavaScript 运算符实用指南之从基础到位运算
作者:San30.
本文全面解析 JavaScript 中的各种运算符,包含丰富的代码示例和详细的知识点讲解。
一、运算符基础概念
在 JavaScript 中,运算符是用于执行数据运算的符号,而参与运算的数据称为操作数。
操作符类型
按操作数数量分类:
- 一元运算符:只有一个操作数,如
++
、--
、typeof
、void
- 二元运算符:有两个操作数,如
+
、-
、*
、/
、=
、&&
、||
- 三元运算符:有三个操作数,即条件运算符
? :
按功能分类:
- 算术运算符
- 比较运算符
- 逻辑运算符
- 位运算符
- 赋值运算符
- 其他特殊运算符
表达式与返回值
每个表达式都会产生一个运算结果,称为返回值,而返回值的类型称为返回类型。
let a = 5; // 赋值表达式返回赋值的结果 let b = a + 3; // 算术表达式返回运算结果 console.log(b); // 函数调用表达式返回undefined
知识点: Chrome浏览器控制台是REPL环境(Read-Eval-Print-Loop),会自动输出表达式的返回值。
二、算术运算符
JavaScript 提供以下算术运算符:
+
、-
、*
、/
:基本四则运算%
:求余(取模)++
、--
:自增自减**
:幂运算(ES6引入)
2.1 算术运算的特殊情况
数字运算的不精确性:
console.log(0.1 + 0.2); // 0.30000000000000004 console.log(0.1 + 0.2 === 0.3); // false
知识点: 这是由于浮点数在计算机中的二进制表示导致的精度问题,并非JavaScript独有。
除数为0的情况:
console.log(5 / 0); // Infinity(正无穷) console.log(-5 / 0); // -Infinity(负无穷) console.log(0 / 0); // NaN(非数字) // 判断特殊值 console.log(isNaN(0 / 0)); // true console.log(isFinite(5 / 0)); // false
知识点:
isNaN()
函数用于检测一个值是否为NaNisFinite()
函数用于检测一个值是否为有限数typeof
运算符返回类型的字符串表示
2.2 类型转换规则
非加号运算符的转换规则:
console.log(10 - true); // 9 (true转换为1) console.log(10 - "5"); // 5 ("5"转换为5) console.log(10 - "abc"); // NaN(转换失败) console.log(10 - ""); // 10 (空字符串转换为0) console.log(10 - null); // 10 (null转换为0) console.log(10 - undefined); // NaN // 对象转换 let obj = {}; console.log(10 - obj); // NaN (obj→"[object Object]"→NaN)
知识点: 除加号外的算术运算符会将非数字类型隐式转换为数字类型。
加号运算符的特殊规则:
// 一边有字符串时进行字符串拼接 console.log(10 + "5"); // "105"(数字转换为字符串) console.log(true + "abc"); // "trueabc"(布尔值转换为字符串) // 两边都没有字符串但有对象 console.log(10 + {}); // "10[object Object]"(对象转换为字符串) // 两边都没有字符串和对象 console.log(true + 5); // 6 (true转换为1) console.log(null + 5); // 5 (null转换为0)
知识点: 加号运算符在有字符串参与时会优先进行字符串拼接,否则进行数学加法运算。
三、自增和自减运算符
自增(++
)和自减(--
)运算符是一元运算符,用于将变量的值增加或减少1。
3.1 前缀与后缀的区别
let x = 5; let y = x++; // 后缀:先赋值后自增 console.log(x, y); // 6, 5 let a = 5; let b = ++a; // 前缀:先自增后赋值 console.log(a, b); // 6, 6
知识点: 前缀形式返回自增后的值,后缀形式返回自增前的值。
3.2 优先级问题
运算符优先级从高到低:
++
、--
*
、/
、%
+
、-
let x = 5; let result = x++ * 2 + 3; // 5*2+3=13,然后x自增为6 console.log(result, x); // 13, 6
知识点: 优先级决定了表达式中运算的执行顺序,同级运算符从左到右计算。
四、比较运算符
比较运算符用于比较两个值,返回布尔值(true
或false
)。
4.1 大小比较运算符
>
、<
、>=
、<=
比较规则详解:
// 字符串比较(按字符编码逐位比较) console.log("a" < "b"); // true(字符a的编码小于b) console.log("2" > "10"); // true(字符"2"编码大于"1") // 类型不同时转换为数字比较 console.log("10" > 5); // true ("10"转换为10) console.log("abc" > 5); // false ("abc"转换为NaN) // 特殊值比较规则 console.log(NaN > 5); // false(NaN与任何值比较都是false) console.log(Infinity > 1000); // true(无穷大比任何有限数大) console.log(-Infinity < -1000); // true(负无穷比任何有限数小) // 对象比较(先转换为原始类型) let obj1 = {}, obj2 = {}; console.log(obj1 > obj2); // false (转换为"[object Object]") console.log(obj1 == obj2); // false (比较的是引用地址)
知识点:
- 字符串比较基于Unicode编码值
- NaN与任何值(包括自身)比较都返回false
- 对象比较时先调用
valueOf()
或toString()
方法转换为原始类型
4.2 相等比较运算符
==
、!=
、===
、!==
==
和!=
的隐式类型转换规则:
// null和undefined的特殊规则 console.log(null == undefined); // true(它们只与彼此相等) console.log(null == 0); // false(null只与undefined相等) // 类型不同时的数字转换 console.log("5" == 5); // true(字符串转换为数字) console.log(true == 1); // true(布尔值转换为数字) console.log("" == 0); // true(空字符串转换为0) // NaN的特殊性 console.log(NaN == NaN); // false(NaN不等于任何值,包括自身) // 对象比较引用地址 console.log({} == {}); // false(不同的对象实例)
知识点: ==
运算符会进行类型转换,遵循复杂的转换规则。
===
和!==
的严格比较:
// 严格相等要求值和类型都相同 console.log("5" === 5); // false(类型不同) console.log(5 === 5); // true(值和类型都相同) // 特殊值的严格比较 console.log(NaN === NaN); // false console.log(null === undefined); // false console.log(0 === -0); // true(特殊情况)
知识点:
===
不进行类型转换,直接比较值和类型- 在实际开发中推荐使用
===
和!==
,避免隐式转换带来的意外结果
五、逻辑运算符
逻辑运算符用于布尔运算,但也可以用于非布尔值,返回的不一定是布尔值。
5.1 逻辑与(&&)
// 布尔运算 console.log(true && false); // false // 短路特性:如果第一个操作数为假,直接返回,不计算第二个 console.log(false && console.log("不会执行")); // false // 非布尔值运算:返回第一个为假的值,或最后一个为真的值 console.log(0 && "abc"); // 0(第一个假值) console.log("abc" && 123); // 123(最后一个真值) console.log(null && "abc"); // null(第一个假值)
知识点:
- 逻辑与的短路特性可用于条件执行
- 返回的是操作数本身,不是强制转换为布尔值
5.2 逻辑或(||)
// 布尔运算 console.log(true || false); // true // 短路特性:如果第一个操作数为真,直接返回,不计算第二个 console.log(true || console.log("不会执行")); // true // 非布尔值运算:返回第一个为真的值,或最后一个为假的值 console.log(0 || "abc"); // "abc"(第一个真值) console.log("abc" || 123); // "abc"(第一个真值) console.log(null || undefined); // undefined(最后一个假值)
知识点: 逻辑或常用于设置默认值
5.3 逻辑非(!)
// 对值进行布尔取反 console.log(!true); // false console.log(!0); // true console.log(!"abc"); // false // 双重非运算可用于将值转换为布尔类型 console.log(!!0); // false console.log(!!"abc"); // true
假值列表(以下值在布尔上下文中被视为false):
null
undefined
false
NaN
''
(空字符串)0
(包括+0和-0)
知识点: 所有对象(包括空对象和数组)在布尔上下文中都被视为true。
六、条件(三目)运算符
条件运算符是JavaScript中唯一的三元运算符。
// 基本语法:条件 ? 表达式1 : 表达式2 let age = 20; let message = age >= 18 ? "成年人" : "未成年人"; console.log(message); // "成年人" // 嵌套使用(可读性较差,不建议过多嵌套) let score = 85; let grade = score >= 90 ? "优秀" : score >= 80 ? "良好" : score >= 60 ? "及格" : "不及格"; console.log(grade); // "良好"
知识点:
- 条件运算符是if-else语句的简洁替代
- 过度嵌套会降低代码可读性,应谨慎使用
七、位运算符
位运算符将操作数视为32位二进制整数进行运算,小数部分会被截断。
7.1 位与(&)、位或(|)、位异或(^)
// 位与:两位都为1时结果为1 console.log(5 & 3); // 1 (0101 & 0011 = 0001) // 位或:有一位为1时结果为1 console.log(5 | 3); // 7 (0101 | 0011 = 0111) // 位异或:两位不同时结果为1 console.log(5 ^ 3); // 6 (0101 ^ 0011 = 0110)
知识点: 位运算前会将操作数转换为32位整数,小数部分被丢弃。
7.2 位非(~)和负数表示
// 位非:按位取反 console.log(~5); // -6 // 快速取整技巧 console.log(~~3.14); // 3 console.log(~~-2.7); // -2
知识点:
- JavaScript使用二进制补码表示负数
~x
等价于-x - 1
~~
可用于快速取整,效果类似Math.trunc()
7.3 位移运算符
// 左移:<< (相当于乘以2的n次方) console.log(5 << 2); // 20 (相当于5*4) // 有符号右移:>> (相当于除以2的n次方并取整) console.log(20 >> 2); // 5 (相当于20/4) // 无符号右移:>>> (符号位也参与位移) console.log(-20 >>> 2); // 1073741819
知识点:
- 左移n位相当于乘以2ⁿ
- 右移n位相当于除以2ⁿ并向下取整
- 无符号右移会将符号位当作普通位处理
7.4 位运算的实际应用
权限控制系统:
// 定义权限常量(使用2的幂次方) const READ = 1; // 0001 const WRITE = 2; // 0010 const EXECUTE = 4; // 0100 const DELETE = 8; // 1000 // 用户权限(使用位或组合权限) let userPermissions = READ | WRITE; // 0011 (3) // 检查权限函数 function hasPermission(permissions, permission) { return (permissions & permission) === permission; } console.log(hasPermission(userPermissions, READ)); // true console.log(hasPermission(userPermissions, EXECUTE)); // false // 添加权限(位或操作) userPermissions |= EXECUTE; console.log(hasPermission(userPermissions, EXECUTE)); // true // 移除权限(位与和位非操作) userPermissions &= ~WRITE; console.log(hasPermission(userPermissions, WRITE)); // false
知识点: 位运算在权限控制、标志位处理等场景中非常高效。
八、其他重要运算符
8.1 模板字符串运算符
let name = "张三"; let age = 25; // 使用反引号和${}插入表达式 console.log(`我叫${name},今年${age}岁`); // 我叫张三,今年25岁 console.log(`明年我就${age + 1}岁了`); // 明年我就26岁了
知识点: 模板字符串支持多行文本和表达式插值。
8.2 复合赋值运算符
let x = 10; x += 5; // 等同于 x = x + 5 console.log(x); // 15 x **= 2; // 等同于 x = x ** 2 console.log(x); // 225 // 其他复合赋值运算符 let y = 10; y -= 3; // y = 7 y *= 2; // y = 14 y /= 7; // y = 2 y %= 2; // y = 0
知识点: 复合赋值运算符是语法糖,使代码更简洁。
8.3 void运算符
// void表达式总是返回undefined console.log(void 0); // undefined console.log(void (5 + 3)); // undefined // 常用于立即执行函数避免污染全局空间 void function() { console.log("立即执行函数"); }(); // 用于箭头函数返回undefined const doSomething = () => void console.log("执行了");
知识点: void运算符确保表达式执行但返回undefined。
8.4 typeof运算符
console.log(typeof 42); // "number" console.log(typeof "hello"); // "string" console.log(typeof true); // "boolean" console.log(typeof undefined); // "undefined" console.log(typeof null); // "object" (历史遗留问题) console.log(typeof []); // "object" console.log(typeof {}); // "object" console.log(typeof function() {}); // "function" // 括号可选 console.log(typeof(42)); // "number" console.log(typeof 42); // "number"
知识点:
typeof null
返回"object"是JavaScript著名的历史bug- 函数有特殊的类型"function"
- 数组和普通对象都返回"object"
8.5 逗号运算符
// 依次执行表达式,返回最后一个表达式的值 let a = (1, 2, 3, 4); console.log(a); // 4 // 用于for循环中的多个变量 for(let i = 0, j = 10; i < j; i++, j--) { console.log(i, j); } // 在一行中执行多个操作 let x = 0, y = 0, z = 0; x = (y++, z++, y + z); // y和z自增,然后x赋值为y+z console.log(x, y, z); // 2, 1, 1
知识点: 逗号运算符优先级最低,常用于需要在一行中执行多个操作的场景。
九、求余与求模的区别
虽然JavaScript中的%
运算符被称为"求模运算符",但实际上它实现的是求余运算。
求余(rem)与求模(mod)的数学区别:
- 求余:符号与被除数相同
- 求模:符号与除数相同
// 正数情况下两者结果相同 console.log(5 % 3); // 2 (余数) // 5 rem 3 = 5 - 1*3 = 2 (商向0取整) // 5 mod 3 = 5 - 1*3 = 2 (商向下取整) // 负数情况下结果不同 console.log(-5 % 3); // -2 (余数,符号与被除数-5相同) // -5 rem 3 = -5 - (-1)*3 = -2 (商向0取整为-1) // -5 mod 3 = -5 - (-2)*3 = 1 (商向下取整为-2) // JavaScript没有内置的求模运算,需要自行实现 function mod(n, m) { return ((n % m) + m) % m; } console.log(mod(-5, 3)); // 1 (模数,符号与除数3相同)
知识点: 在数学和计算机科学中,求余和求模是不同的概念,JavaScript的%
实现的是求余运算。
十、运算符优先级总结
JavaScript运算符优先级从高到低大致为:
- 成员访问:
.
[]
- new(带参数列表)
- 函数调用:
()
- new(无参数列表)
- 后置递增/递减:
++
--
- 逻辑非:
!
位非:~
typeof void delete await - 幂运算:
**
- 乘除取余:
*
/
%
- 加减:
+
-
- 位移:
<<
>>
>>>
- 关系比较:
<
<=
>
>=
in
instanceof
- 相等比较:
==
!=
===
!==
- 位与:
&
- 位异或:
^
- 位或:
|
- 逻辑与:
&&
- 逻辑或:
||
- 条件运算符:
? :
- 赋值:
=
+=
-=
等 - 逗号运算符:
,
知识点: 使用括号可以明确优先级,提高代码可读性。
十一、综合应用示例
利用运算符实现RGB颜色值的提取:
function getRGBComponents(color) { // 使用位移和位与提取颜色分量 let r = (color >> 16) & 0xFF; // 提取红色分量 let g = (color >> 8) & 0xFF; // 提取绿色分量 let b = color & 0xFF; // 提取蓝色分量 return [r, g, b]; } let color = 0xFF3366; // 粉色 let [r, g, b] = getRGBComponents(color); console.log(`R: ${r}, G: ${g}, B: ${b}`); // R: 255, G: 51, B: 102
安全的属性访问:
// 使用逻辑或设置默认值 function getUserName(user) { return user && user.name || "匿名用户"; } console.log(getUserName({name: "张三"})); // "张三" console.log(getUserName(null)); // "匿名用户" // 使用可选链运算符(ES2020)和空值合并运算符(ES2020) function getUserNameSafe(user) { return user?.name ?? "匿名用户"; }
利用位运算判断奇偶性:
function isEven(num) { return (num & 1) === 0; // 最后一位为0则是偶数 } console.log(isEven(4)); // true console.log(isEven(7)); // false
总结
JavaScript提供了丰富的运算符,从基本的算术运算到复杂的位运算,掌握这些运算符的特性和使用场景对于编写高效、简洁的代码至关重要。关键知识点包括:
- 理解隐式类型转换:特别是在使用
==
和+
运算符时 - 掌握运算符优先级:使用括号明确运算顺序,提高代码可读性
- 利用短路特性:
&&
和||
的短路特性可以简化条件逻辑 - 位运算的高效应用:在权限控制、标志位处理等场景中非常有用
- 推荐使用严格相等:
===
和!==
比==
和!=
更安全可靠 - 模板字符串的现代用法:简化字符串拼接和插值
希望本文能帮助你全面理解JavaScript运算符,并在实际开发中灵活运用它们,写出更优雅、高效的代码。
到此这篇关于JavaScript 运算符完全指南:从基础到位运算的文章就介绍到这了,更多相关js位运算内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!