运算符与表达式

本章将详细介绍ECMAScript中的各种运算符和表达式,包括算术运算符、比较运算符、逻辑运算符等,以及它们的优先级和结合性。

6.1 运算符概述

运算符是用于执行程序代码运算的符号,它们可以对一个或多个操作数进行操作并产生结果。ECMAScript提供了丰富的运算符来满足不同的运算需求。

6.2 算术运算符

算术运算符用于执行基本的数学运算。

基本算术运算符

// 加法
let sum = 5 + 3; // 8

// 减法
let difference = 10 - 4; // 6

// 乘法
let product = 7 * 6; // 42

// 除法
let quotient = 15 / 3; // 5

// 取余
let remainder = 17 % 5; // 2

// 指数运算 (ES2016)
let power = 2 ** 3; // 8

递增和递减运算符

// 前置递增
let a = 5;
let b = ++a; // a = 6, b = 6

// 后置递增
let c = 5;
let d = c++; // c = 6, d = 5

// 前置递减
let e = 10;
let f = --e; // e = 9, f = 9

// 后置递减
let g = 10;
let h = g--; // g = 9, h = 10

一元运算符

// 正号
let positive = +5; // 5

// 负号
let negative = -5; // -5

// 类型转换
let str = "123";
let num = +str; // 123 (转换为数字)

// 逻辑非
let truthy = !"hello"; // false
let falsy = !0; // true

6.3 比较运算符

比较运算符用于比较两个值并返回布尔值。

相等比较运算符

// 相等 (==) - 进行类型转换
console.log(5 == "5"); // true
console.log(true == 1); // true
console.log(null == undefined); // true

// 不相等 (!=) - 进行类型转换
console.log(5 != "6"); // true

// 严格相等 (===) - 不进行类型转换
console.log(5 === 5); // true
console.log(5 === "5"); // false

// 严格不相等 (!==) - 不进行类型转换
console.log(5 !== "5"); // true

关系比较运算符

// 大于
console.log(5 > 3); // true

// 小于
console.log(3 < 5); // true

// 大于等于
console.log(5 >= 5); // true

// 小于等于
console.log(3 <= 5); // true

6.4 逻辑运算符

逻辑运算符用于组合多个布尔值表达式。

基本逻辑运算符

// 逻辑与 (&&)
let andResult = true && false; // false
let andResult2 = "hello" && 42; // 42 (短路求值)

// 逻辑或 (||)
let orResult = true || false; // true
let orResult2 = "" || "default"; // "default" (短路求值)

// 逻辑非 (!)
let notResult = !true; // false
let notResult2 = !"hello"; // false

空值合并运算符 (??) - ES2020

// 只有当左侧为null或undefined时才返回右侧值
let value1 = null ?? "default"; // "default"
let value2 = "" ?? "default"; // "" (空字符串不是null或undefined)
let value3 = 0 ?? "default"; // 0 (0不是null或undefined)

可选链运算符 (?.) - ES2020

// 安全访问嵌套属性
let user = {
    name: "张三",
    address: {
        city: "北京"
    }
};

let city = user?.address?.city; // "北京"
let country = user?.address?.country; // undefined (不会报错)
let phone = user?.phone?.number; // undefined (不会报错)

6.5 位运算符

位运算符用于对数字的二进制表示进行操作。

// 按位与 (&)
let and = 5 & 3; // 1 (101 & 011 = 001)

// 按位或 (|)
let or = 5 | 3; // 7 (101 | 011 = 111)

// 按位异或 (^)
let xor = 5 ^ 3; // 6 (101 ^ 011 = 110)

// 按位非 (~)
let not = ~5; // -6

// 左移 (<<)
let leftShift = 5 << 1; // 10 (101 << 1 = 1010)

// 右移 (>>)
let rightShift = 5 >> 1; // 2 (101 >> 1 = 10)

// 无符号右移 (>>>)
let unsignedRightShift = -5 >>> 1; // 2147483645

6.6 赋值运算符

赋值运算符用于给变量赋值。

基本赋值运算符

// 简单赋值
let x = 10;

// 复合赋值运算符
x += 5; // x = x + 5
x -= 3; // x = x - 3
x *= 2; // x = x * 2
x /= 4; // x = x / 4
x %= 3; // x = x % 3
x **= 2; // x = x ** 2 (ES2016)
x <<= 1; // x = x << 1
x >>= 1; // x = x >> 1
x >>>= 1; // x = x >>> 1
x &= 3; // x = x & 3
x |= 3; // x = x | 3
x ^= 3; // x = x ^ 3

6.7 运算符优先级

运算符优先级决定了表达式中运算的执行顺序。优先级高的运算符会先执行。

优先级示例

// 乘法优先级高于加法
let result1 = 2 + 3 * 4; // 14 (先算3*4=12,再算2+12=14)

// 使用括号改变优先级
let result2 = (2 + 3) * 4; // 20 (先算2+3=5,再算5*4=20)

// 逻辑运算符优先级
let result3 = true || false && false; // true (&&优先级高于||)

常用运算符优先级表

优先级 运算符 结合性
20 () [] . 从左到右
19 ++ -- (后置) 从左到右
18 ++ -- (前置) ! ~ + - typeof void delete 从右到左
17 ** (指数) 从右到左
16 * / % 从左到右
15 + - 从左到右
12 <> <=>= 从左到右
11 == != === !== 从左到右
7 && 从左到右
6 || 从左到右
4 ? : 从右到左
3 = += -= *= /= %= **= <<=>>= &= ^= |= 从右到左

6.8 表达式

表达式是由变量、常量、运算符和函数调用组成的代码片段,它会产生一个值。

表达式类型

// 算术表达式
let arithmetic = 5 + 3 * 2;

// 字符串表达式
let stringExpr = "Hello" + " " + "World";

// 逻辑表达式
let logical = (5 > 3) && (2 < 4);

// 函数调用表达式
let funcResult = Math.max(5, 10, 3);

// 对象创建表达式
let obj = {name: "张三", age: 25};

// 数组创建表达式
let arr = [1, 2, 3, 4];

// 条件表达式 (三元运算符)
let status = age >= 18 ? "成年人" : "未成年人";

6.9 运算符的实际应用

条件赋值

// 使用逻辑或设置默认值
let userName = inputName || "匿名用户";

// 使用空值合并运算符设置默认值
let userAge = inputAge ?? 18;

// 使用三元运算符进行条件赋值
let discount = isMember ? 0.1 : 0;

链式操作

// 方法链
let result = [1, 2, 3, 4, 5]
    .filter(x => x > 2)  // [3, 4, 5]
    .map(x => x * 2)     // [6, 8, 10]
    .reduce((a, b) => a + b, 0); // 24

提示:理解运算符的优先级和结合性对于编写正确的表达式非常重要。在复杂的表达式中,适当使用括号可以提高代码的可读性和可维护性。