4JavaScript高级程序设计(第4版)

表达式和运算符

运算与求值

运算符表达式求值顺序

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第4章:表达式和运算符(Expressions and Operators)

一、导读

1.1 本章学习目标

第4章讲解JavaScript的表达式和运算符。本章核心目标:

  • 掌握各种运算符:算术、比较、逻辑、赋值运算符
  • 理解短路求值:&& 和 || 的特殊行为
  • 学会使用可选链和空值合并:ES2020的新特性
  • 掌握运算符优先级:复杂表达式如何求值
  • 理解三元运算符:条件表达式的简写

1.2 为什么本章如此重要

运算符是构建表达式的基础。JavaScript的运算符有一些独特的行为(如 + 既做加法又做字符串拼接),理解这些规则才能避免bug。可选链(?.)和空值合并(??)是现代JavaScript的重要特性,大大简化了代码。

1.3 核心问题引导

为什么 + 运算符有时做加法有时做字符串拼接?

?. 可选链解决了什么问题?

?? 和 || 有什么区别?

JavaScript的运算符优先级和C/Java有什么不同?


二、核心概念详解

4.1 算术运算符

javascript
console.log(10 + 3);    // 13
console.log(10 - 3);    // 7
console.log(10 * 3);    // 30
console.log(10 / 3);    // 3.333...
console.log(10 % 3);    // 1(取余)
console.log(2 ** 10);   // 1024(幂运算,ES2016)

// + 运算符的双重行为
console.log(5 + 3);     // 8(数字加法)
console.log("5" + 3);   // "53"(字符串拼接)
console.log(5 + "3");   // "53"
console.log("5" + "3"); // "53"

// 其他运算符触发数字转换
console.log("5" - 3);   // 2
console.log("5" * "2"); // 10
console.log("5" / "2"); // 2.5

4.2 比较运算符

javascript
// 宽松相等(==):会类型转换
console.log(5 == "5");          // true
console.log(0 == false);        // true
console.log(null == undefined); // true
console.log("" == 0);           // true

// 严格相等(===):不转换
console.log(5 === "5");         // false
console.log(0 === false);       // false
console.log(null === undefined); // false

// 推荐:始终使用 ===
// NaN的特殊性
console.log(NaN === NaN);       // false
console.log(Object.is(NaN, NaN)); // true

// 对象比较(引用比较)
let a = [1, 2];
let b = [1, 2];
console.log(a === b);  // false(不同引用)
console.log(a == b);   // false

4.3 逻辑运算符

javascript
// && 和 || 返回操作数的值(不只是true/false)
console.log(0 || "hello");     // "hello"
console.log("" || "default");  // "default"
console.log(null || 42);       // 42
console.log(1 && "hello");     // "hello"
console.log(0 && "hello");     // 0

// 短路求值
let user = null;
let name = user && user.name;  // null(不会报错)

// ! 取反
console.log(!true);    // false
console.log(!0);       // true
console.log(!"");      // true
console.log(!null);    // true

// 双重取反:转为布尔值
console.log(!!0);      // false
console.log(!!"hello"); // true
console.log(!!null);   // false

4.4 可选链和空值合并

javascript
// 可选链(?.)ES2020
let user = {
  name: "Alice",
  address: {
    city: "Beijing"
  }
};

console.log(user.address?.city);    // "Beijing"
console.log(user.phone?.number);    // undefined(不会报错)
console.log(user.address?.zip?.code); // undefined

// 可选链用于函数调用
let obj = {
  greet: function() { return "Hello"; }
};
console.log(obj.greet?.());   // "Hello"
console.log(obj.farewell?.()); // undefined

// 可选链用于数组
let arr = [1, 2, 3];
console.log(arr?.[0]);  // 1
console.log(arr?.[10]); // undefined

// 空值合并(??)ES2020
// 只在左侧为null或undefined时返回右侧值
let value1 = null ?? "default";
console.log(value1);  // "default"

let value2 = 0 ?? "default";
console.log(value2);  // 0(0不是null/undefined)

let value3 = "" ?? "default";
console.log(value3);  // ""(空字符串不是null/undefined)

// 对比 ||
console.log(0 || "default");   // "default"(0是falsy)
console.log(0 ?? "default");   // 0(0不是null/undefined)

4.5 三元运算符

javascript
let age = 20;
let status = age >= 18 ? "adult" : "minor";
console.log(status);  // "adult"

// 嵌套三元(不推荐,可读性差)
let score = 85;
let grade = score >= 90 ? "A" :
            score >= 80 ? "B" :
            score >= 70 ? "C" : "D";
console.log(grade);  // "B"

// 在JSX中使用
// <div>{isLoggedIn ? <Dashboard /> : <Login />}</div>

4.6 赋值运算符

javascript
let x = 10;
x += 5;   // x = x + 5 = 15
x -= 3;   // x = x - 3 = 12
x *= 2;   // x = x * 2 = 24
x /= 4;   // x = x / 4 = 6
x %= 4;   // x = x % 4 = 2
x **= 3;  // x = x ** 3 = 8

// 逻辑赋值(ES2021)
let a = null;
a ||= "default";    // a = a || "default"
console.log(a);     // "default"

let b = 0;
b ??= 42;           // b = b ?? 42
console.log(b);     // 42

let c = 1;
c &&= 2;            // c = c && 2
console.log(c);     // 2

4.7 运算符优先级

javascript
// 常见优先级(从高到低)
// 1. () 括号
// 2. ** 幂
// 3. * / % 乘除余
// 4. + - 加减
// 5. < > <= >= 比较
// 6. === !== 相等
// 7. && 逻辑与
// 8. || 逻辑或
// 9. ?? 空值合并
// 10. = += -= 赋值

let result = 2 + 3 * 4;       // 14
let result2 = (2 + 3) * 4;    // 20
let result3 = 2 ** 3 ** 2;    // 512(右结合:2^(3^2))

// 逗号运算符
let val = (1, 2, 3);  // val = 3
console.log(val);

三、实战案例

4.1 安全访问深层对象

javascript
// 不使用可选链(繁琐)
let city1;
if (user && user.address && user.address.city) {
  city1 = user.address.city;
}

// 使用可选链(简洁)
let city2 = user?.address?.city;

// 提供默认值
let city3 = user?.address?.city ?? "Unknown";

4.2 条件渲染

javascript
// 根据条件选择不同值
function getDiscount(isMember, purchaseAmount) {
  return isMember 
    ? (purchaseAmount > 100 ? 0.2 : 0.1)
    : (purchaseAmount > 100 ? 0.05 : 0);
}

console.log(getDiscount(true, 150));   // 0.2
console.log(getDiscount(false, 50));   // 0

四、本章小结

第4章讲解了JavaScript的表达式和运算符:

算术运算符:+ 既做加法又做字符串拼接,注意类型转换。

比较运算符:推荐使用 ===,避免 == 的类型转换陷阱。

逻辑运算符:&& 和 || 返回操作数的值,支持短路求值。

可选链(?.):安全访问可能不存在的属性。

空值合并(??):只在null/undefined时提供默认值。

运算符优先级:使用括号明确意图。


五、思考与练习

思考题

为什么 + 运算符有双重行为?这是设计缺陷还是有意为之?

?? 和 || 在什么场景下行为不同?

可选链可以用于哪些场景?

为什么 NaN === NaN 返回 false?

编程练习

使用可选链和空值合并重写一段嵌套的if-else代码。

编写一个函数,安全地访问任意深度的对象属性。

实现一个简单的表达式计算器。


4/14表达式和
0%