深入理解 JavaScript 运算符:构建代码逻辑的基石
在 JavaScript 编程中,运算符(Operators)是执行特定操作的符号或关键字,它们允许我们操纵值、执行计算、进行比较、组合表达式以及控制程序流程。它们是编写任何 JavaScript 代码的基础构建块,理解不同类型的运算符及其行为对于编写正确、高效且可读的代码至关重要。从简单的算术运算到复杂的逻辑判断和现代的属性访问,运算符无处不在。
本教程将全面深入地探讨 JavaScript 中各种类型的运算符。我们将从最基本的算术运算符开始,逐步深入到赋值运算符、比较运算符、逻辑运算符、位运算符、字符串运算符、条件(三元)运算符以及类型运算符。此外,我们还将重点关注 ECMAScript 2015 (ES6) 及以后版本引入的一些现代且极其实用的运算符,如展开/剩余运算符、可选链运算符和空值合并运算符。最后,我们将讨论运算符的优先级和结合性,以及使用运算符时的最佳实践和常见陷阱。通过掌握这些核心概念,您将能够精准地控制代码中的数据处理和逻辑判断,从而编写出更强大、更优雅的 JavaScript 应用程序。
1. 什么是 JavaScript 运算符?
JavaScript 运算符是特殊的符号或关键字,用于对一个或多个值(操作数)执行操作,并返回一个结果。这些操作可以是算术计算、逻辑判断、赋值操作或更复杂的数据结构操作。
根据操作数的数量,运算符通常分为:
- 一元运算符 (Unary Operators): 操作一个操作数(例如
++x,-y)。 - 二元运算符 (Binary Operators): 操作两个操作数(例如
x + y,a === b)。 - 三元运算符 (Ternary Operators): 操作三个操作数(JavaScript 中特指条件运算符
? :)。
2. JavaScript 运算符的类型
JavaScript 提供了多种类型的运算符,每种都有其特定的用途。
2.1 算术运算符 (Arithmetic Operators)
用于执行数学计算。
| 运算符 | 描述 | 示例 | 结果 |
|---|---|---|---|
+ |
加法 | 5 + 3 |
8 |
- |
减法 | 5 - 3 |
2 |
* |
乘法 | 5 * 3 |
15 |
/ |
除法 | 5 / 3 |
1.666... |
% |
取模 (求余数) | 5 % 3 |
2 |
** |
幂 (ES2016) | 2 ** 3 (2的3次方) |
8 |
++ |
自增 | let x = 5; x++; 或 ++x; |
x 变为 6 |
-- |
自减 | let x = 5; x--; 或 --x; |
x 变为 4 |
注意:
+运算符如果有一个操作数是字符串,则会执行字符串连接操作。console.log(5 + "3"); // "53" console.log("Hello " + "World"); // "Hello World"- 自增/自减运算符的前缀 (
++x) 和后缀 (x++) 形式在表达式中的返回值有所不同:- 前缀: 先改变值,再返回新值。
let a = 5; let b = ++a; // a 先变为 6,然后 b 得到 6 console.log(a); // 6 console.log(b); // 6 - 后缀: 先返回原值,再改变值。
let x = 5; let y = x++; // y 得到 5,然后 x 变为 6 console.log(x); // 6 console.log(y); // 5
- 前缀: 先改变值,再返回新值。
2.2 赋值运算符 (Assignment Operators)
用于给变量赋值。
| 运算符 | 示例 | 等同于 |
|---|---|---|
= |
x = 5 |
x = 5 |
+= |
x += 5 |
x = x + 5 |
-= |
x -= 5 |
x = x - 5 |
*= |
x *= 5 |
x = x * 5 |
/= |
x /= 5 |
x = x / 5 |
%= |
x %= 5 |
x = x % 5 |
**= |
x **= 5 (ES2016) |
x = x ** 5 |
&&= |
x &&= y (ES2021) |
x = x && y |
| ` | =` | |
??= |
x ??= y (ES2021) |
x = x ?? y |
逻辑赋值运算符 (Logical Assignment Operators):&&=, ||=, ??=
这些运算符结合了逻辑操作和赋值操作,提供了一种更简洁的写法。
x &&= y: 如果x为真值 (truthy),则x = y。x ||= y: 如果x为假值 (falsy),则x = y。x ??= y: 如果x为null或undefined,则x = y。
let a = 10;
a &&= 20; // a 是真值,所以 a = 20
console.log(a); // 20
let b = 0;
b ||= 30; // b 是假值,所以 b = 30
console.log(b); // 30
let c = null;
c ??= 40; // c 是 null,所以 c = 40
console.log(c); // 40
2.3 比较运算符 (Comparison Operators)
用于比较两个值,并返回一个布尔值 (true 或 false)。
| 运算符 | 描述 | 示例 | 结果 |
|---|---|---|---|
== |
相等 (只比较值,不比较类型) | 5 == "5" |
true |
=== |
严格相等 (比较值和类型) | 5 === "5" |
false |
!= |
不相等 (只比较值) | 5 != "5" |
false |
!== |
严格不相等 (比较值和类型) | 5 !== "5" |
true |
> |
大于 | 5 > 3 |
true |
< |
小于 | 5 < 3 |
false |
>= |
大于或等于 | 5 >= 5 |
true |
<= |
小于或等于 | 5 <= 3 |
false |
重要区别:== vs ===
这是 JavaScript 中最常见的困惑点之一。
==(宽松相等) 会在比较之前进行类型转换 (type coercion),试图将操作数转换为相同的类型再进行比较。这可能导致一些非直观的结果。console.log(0 == false); // true (0 被转换为 false) console.log("0" == false); // true ("0" 被转换为 0,0 被转换为 false) console.log(null == undefined); // true===(严格相等) 不会进行类型转换。如果操作数的类型不同,则直接返回false。推荐在大多数情况下使用===,以避免隐式类型转换带来的意外行为。console.log(0 === false); // false console.log("0" === false); // false console.log(null === undefined); // false
2.4 逻辑运算符 (Logical Operators)
用于组合布尔表达式,并返回一个布尔值。
| 运算符 | 描述 | 示例 |
|---|---|---|
&& |
逻辑与 (AND) | true && false |
| ` | ` | |
! |
逻辑非 (NOT) | !true |
逻辑运算符的短路 (Short-Circuit) 行为:
&&(AND):如果第一个操作数是假值,则不再评估第二个操作数,并返回第一个操作数的值。否则返回第二个操作数的值。常用于条件执行。let result = condition1 && doSomething(); // 如果 condition1 为 false,doSomething() 不会被执行 console.log(10 && "Hello"); // "Hello" (10 是真值,返回第二个) console.log(0 && "Hello"); // 0 (0 是假值,返回第一个)||(OR):如果第一个操作数是真值,则不再评估第二个操作数,并返回第一个操作数的值。否则返回第二个操作数的值。常用于设置默认值。let result = value || defaultValue; // 如果 value 是假值,result 将是 defaultValue console.log(10 || "Hello"); // 10 (10 是真值,返回第一个) console.log(0 || "Hello"); // "Hello" (0 是假值,返回第二个)!(NOT):将操作数转换为布尔值,然后取反。console.log(!true); // false console.log(!0); // true (0 被转换为 false,然后取反为 true) console.log(!"Hello"); // false ("Hello" 被转换为 true,然后取反为 false)
2.5 位运算符 (Bitwise Operators)
直接操作其操作数的二进制位。在 JavaScript 中相对不常用,但在特定场景(如权限管理、颜色处理或性能优化)下非常有用。
| 运算符 | 描述 | 示例 (5 是 0101, 3 是 0011) |
结果 | 二进制结果 |
|---|---|---|---|---|
& |
位与 (AND) | 5 & 3 |
1 |
0001 |
| ` | ` | 位或 (OR) | `5 | 3` |
^ |
位异或 (XOR) | 5 ^ 3 |
6 |
0110 |
~ |
位非 (NOT) | ~5 |
-6 |
1111...1010 (补码) |
<< |
左移 | 5 << 1 |
10 |
1010 |
>> |
有符号右移 | 5 >> 1 |
2 |
0010 |
>>> |
无符号右移 | 5 >>> 1 |
2 |
0010 |
2.6 字符串运算符 (String Operators)
除了前面提到的 + 和 += 用于连接字符串,JavaScript 没有其他专门的字符串运算符。
let str1 = "Hello";
let str2 = " World";
let result = str1 + str2; // "Hello World"
str1 += "!"; // str1 变为 "Hello!"
2.7 三元 (条件) 运算符 (Ternary (Conditional) Operator)
这是 JavaScript 中唯一一个三元运算符。它根据条件的结果选择两个表达式中的一个。
- 语法:
condition ? expressionIfTrue : expressionIfFalse - 示例:
它提供了一种比let age = 20; let status = (age >= 18) ? "成年人" : "未成年人"; console.log(status); // "成年人"if-else语句更简洁的条件赋值方式,特别适用于简单的条件表达式。
2.8 类型运算符 (Type Operators)
用于获取变量的类型信息。
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 Symbol('foo')); // "symbol" (ES6) console.log(typeof {}); // "object" console.log(typeof []); // "object" console.log(typeof function(){}); // "function"instanceof: 检查一个对象是否是特定构造函数的实例。let arr = [1, 2, 3]; console.log(arr instanceof Array); // true console.log(arr instanceof Object); // true (因为 Array 继承自 Object) function Car(make, model) { this.make = make; this.model = model; } let myCar = new Car("Honda", "Civic"); console.log(myCar instanceof Car); // true console.log(myCar instanceof Object); // true console.log("Hello" instanceof String); // false (字符串字面量不是 String 对象的实例)
2.9 一元运算符 (Unary Operators)
除了自增/自减,还有其他一些一元运算符。
- 一元加号 (
+): 尝试将其操作数转换为数字。console.log(+"5"); // 5 (number) console.log(+"Hello"); // NaN console.log(+true); // 1 - 一元减号 (
-): 尝试将其操作数转换为数字,然后取反。console.log(-"5"); // -5 (number) console.log(-true); // -1 delete: 用于删除对象的属性。const myObj = { a: 1, b: 2 }; delete myObj.a; console.log(myObj); // { b: 2 } const arr = [1, 2, 3]; delete arr[1]; // 不会改变数组长度,而是将元素置为 undefined console.log(arr); // [1, undefined, 3] console.log(arr.length); // 3void: 评估表达式并返回undefined。常用于在<a>标签中执行 JavaScript 而不刷新页面,或在某些场景下确保表达式返回undefined。console.log(void(0)); // undefined console.log(void(console.log("Hello"))); // 输出 "Hello",然后返回 undefined
2.10 ES6+ 新增运算符
现代 JavaScript 引入了一些非常强大且常用的运算符。
2.10.1 展开/剩余运算符 (...) (Spread/Rest Operator)
- 作为展开运算符 (Spread Syntax): 将可迭代对象(如数组、字符串、Set、Map)展开为独立的元素,或将对象的可枚举属性展开为键值对。
// 数组合并 const arr1 = [1, 2]; const arr2 = [3, 4]; const combined = [...arr1, ...arr2]; // [1, 2, 3, 4] // 数组复制 const original = [1, 2, 3]; const copy = [...original]; // [1, 2, 3] (浅复制) // 函数调用时展开参数 function sum(a, b, c) { return a + b + c; } const nums = [1, 2, 3]; console.log(sum(...nums)); // 6 // 对象合并/复制 (ES2018) const obj1 = { a: 1, b: 2 }; const obj2 = { c: 3, d: 4 }; const merged = { ...obj1, ...obj2 }; // { a: 1, b: 2, c: 3, d: 4 } const copiedObj = { ...obj1 }; // { a: 1, b: 2 } (浅复制) - 作为剩余参数 (Rest Parameters): 用于函数定义中,将不定数量的参数收集到一个数组中。
function logArguments(firstArg, ...restArgs) { console.log("第一个参数:", firstArg); console.log("剩余参数:", restArgs); // restArgs 是一个数组 } logArguments(1, 2, 3, 4, 5); // 输出: // 第一个参数: 1 // 剩余参数: [2, 3, 4, 5]
2.10.2 可选链运算符 (?.) (Optional Chaining) (ES2020)
- 作用: 允许读取位于连接对象链深处的属性,而无需明确验证链中的每个引用是否有效。如果引用是
null或undefined,表达式会短路返回undefined,而不是抛出错误。 - 语法:
obj?.prop,obj?.[expr],func?.() - 示例:
const user = { name: "Alice", address: { street: "123 Main St", city: "Anytown" }, getDetails() { return `${this.name} from ${this.address.city}`; } }; console.log(user.address?.city); // "Anytown" console.log(user.contact?.email); // undefined (user.contact 是 undefined,不报错) console.log(user.getDetails?.()); // "Alice from Anytown" (安全调用方法) const admin = null; console.log(admin?.name); // undefined
2.10.3 空值合并运算符 (??) (Nullish Coalescing) (ES2020)
- 作用: 提供一个默认值,但仅在左侧操作数是
null或undefined时才使用。与||(逻辑或) 运算符不同,??只检查null或undefined,而||会检查所有假值(0,'',false,null,undefined)。 - 语法:
expression1 ?? expression2 - 示例:
const foo = null ?? 'default string'; console.log(foo); // "default string" const bar = 0 ?? 'default string'; console.log(bar); // 0 (0 不是 null 或 undefined,所以保留 0) const baz = '' ?? 'default string'; console.log(baz); // '' (空字符串不是 null 或 undefined,所以保留空字符串) // 对比 || const qux = 0 || 'default string'; console.log(qux); // "default string" (0 是假值,所以使用默认值)
3. 运算符优先级和结合性
当一个表达式中包含多个运算符时,JavaScript 引擎会根据优先级 (Precedence) 和结合性 (Associativity) 规则来决定运算符的计算顺序。
- 优先级: 决定哪个运算符先执行。例如,乘法
*的优先级高于加法+。let result = 2 + 3 * 4; // 先计算 3 * 4 (12),再计算 2 + 12 (14) console.log(result); // 14 - 结合性: 当多个运算符具有相同的优先级时,结合性决定它们的计算方向(从左到右或从右到左)。例如,加法
+和减法-都是左结合的。let result = 10 - 5 + 2; // (10 - 5) + 2 => 5 + 2 => 7 (从左到右) console.log(result); // 7
你可以通过使用括号 () 来明确指定计算顺序,括号内的表达式总是优先计算,这也有助于提高代码的可读性。
let result = (2 + 3) * 4; // 先计算 (2 + 3) (5),再计算 5 * 4 (20)
console.log(result); // 20
建议: 即使你记住优先级规则,在复杂的表达式中,使用括号来明确意图总是一个好习惯。
4. 最佳实践和常见陷阱
- 始终使用
===和!==: 避免==和!=带来的隐式类型转换,这可以大大减少意外错误和提高代码可预测性。 - 理解逻辑运算符的短路行为: 利用
&&进行条件执行,利用||设置默认值,但要清楚它们何时返回操作数的值而不是纯粹的布尔值。 - 注意副作用: 像
++、--和赋值运算符 (=) 会改变变量的值。在复杂表达式中混合使用它们可能会导致难以调试的问题。尽量将它们放在独立的语句中。let i = 0; // 不推荐在条件中做赋值,可能导致阅读困难或意外 if (i = 5) { // i 被赋值为 5,表达式结果为 5 (真值),所以条件为 true console.log("执行"); // 执行 } - 使用括号来明确优先级: 即使你确定运算符的优先级,使用括号也能让代码意图更清晰,减少混淆。
- 警惕隐式类型转换: JavaScript 在执行某些操作(如
+字符串连接,比较运算符==)时会进行隐式类型转换。了解这些转换规则可以避免错误。console.log(1 + "2" + 3); // "123" (从左到右,1+“2”是字符串连接,然后"12"+3也是字符串连接) - ES6+ 新特性: 积极使用可选链
?.和空值合并??来编写更简洁、更安全的访问嵌套属性和设置默认值的代码。
5. 总结与展望
JavaScript 运算符是语言的骨架,它们赋予我们处理数据、控制逻辑的能力。
- 掌握算术、赋值、比较、逻辑等基本运算符是编写任何程序的起点。
- 理解严格相等
===和短路逻辑&&,||是避免常见陷阱的关键。 - 熟悉优先级和结合性,并善用括号,能确保表达式按预期计算。
- ES6+ 引入的展开/剩余运算符
...、可选链?.和空值合并??等现代运算符,极大地提升了代码的简洁性和安全性,是现代 JavaScript 开发中不可或缺的工具。
通过深入理解和熟练运用这些运算符,您将能够更精确地表达您的编程意图,编写出更清晰、更健壮、更高效的 JavaScript 代码。
更多推荐

所有评论(0)