在 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: 如果 xnullundefined,则 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)

用于比较两个值,并返回一个布尔值 (truefalse)。

运算符 描述 示例 结果
== 相等 (只比较值,不比较类型) 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 中相对不常用,但在特定场景(如权限管理、颜色处理或性能优化)下非常有用。

运算符 描述 示例 (50101, 30011) 结果 二进制结果
& 位与 (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); // 3
    
  • void 评估表达式并返回 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)
  • 作用: 允许读取位于连接对象链深处的属性,而无需明确验证链中的每个引用是否有效。如果引用是 nullundefined,表达式会短路返回 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)
  • 作用: 提供一个默认值,但仅在左侧操作数是 nullundefined 时才使用。与 || (逻辑或) 运算符不同,?? 只检查 nullundefined,而 || 会检查所有假值(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. 最佳实践和常见陷阱

  1. 始终使用 ===!== 避免 ==!= 带来的隐式类型转换,这可以大大减少意外错误和提高代码可预测性。
  2. 理解逻辑运算符的短路行为: 利用 && 进行条件执行,利用 || 设置默认值,但要清楚它们何时返回操作数的值而不是纯粹的布尔值。
  3. 注意副作用:++-- 和赋值运算符 (=) 会改变变量的值。在复杂表达式中混合使用它们可能会导致难以调试的问题。尽量将它们放在独立的语句中。
    let i = 0;
    // 不推荐在条件中做赋值,可能导致阅读困难或意外
    if (i = 5) { // i 被赋值为 5,表达式结果为 5 (真值),所以条件为 true
        console.log("执行"); // 执行
    }
    
  4. 使用括号来明确优先级: 即使你确定运算符的优先级,使用括号也能让代码意图更清晰,减少混淆。
  5. 警惕隐式类型转换: JavaScript 在执行某些操作(如 + 字符串连接,比较运算符 ==)时会进行隐式类型转换。了解这些转换规则可以避免错误。
    console.log(1 + "2" + 3); // "123" (从左到右,1+“2”是字符串连接,然后"12"+3也是字符串连接)
    
  6. ES6+ 新特性: 积极使用可选链 ?. 和空值合并 ?? 来编写更简洁、更安全的访问嵌套属性和设置默认值的代码。

5. 总结与展望

JavaScript 运算符是语言的骨架,它们赋予我们处理数据、控制逻辑的能力。

  • 掌握算术、赋值、比较、逻辑等基本运算符是编写任何程序的起点。
  • 理解严格相等 ===短路逻辑 &&, || 是避免常见陷阱的关键。
  • 熟悉优先级和结合性,并善用括号,能确保表达式按预期计算。
  • ES6+ 引入的展开/剩余运算符 ...可选链 ?.空值合并 ?? 等现代运算符,极大地提升了代码的简洁性和安全性,是现代 JavaScript 开发中不可或缺的工具。

通过深入理解和熟练运用这些运算符,您将能够更精确地表达您的编程意图,编写出更清晰、更健壮、更高效的 JavaScript 代码。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐