JavaScript 运算符与类型转换

AI 辅助生成本文由 AI 起草并经人工核查,示例均标明软件版本;发现错误请以官方文档为准。

Node.js 22 LTS

JavaScript 的比较和取值之所以容易出错,多半不是运算符难,而是隐式类型转换在你没注意到的时候发生了。这一篇把规则摊开讲清楚。

== 与 ===

1 == "1"      // true   (先转换再比较)
1 === "1"     // false  (类型不同直接不相等)
  • ==(宽松相等):类型不同时先做隐式转换再比较。
  • ===(严格相等):类型不同直接返回 false,不做转换。

结论:一律用 ===,只在确实需要「null 和 undefined 视为等价」时用 == null 这种写法。

== 的转换规则复杂到不建议记忆,看几个真实结果就够了:

"" == 0          // true
"0" == 0         // true
false == "0"     // true
null == 0        // false
null == undefined // true
[] == false      // true

truthy 与 falsy

需要布尔值的地方(if、&&、||、!),JavaScript 会把值转成布尔。只有下面 8 个值是 falsy,其余全是 truthy:

false, 0, -0, 0n, "", null, undefined, NaN

注意其中的反直觉项:

if ("0") console.log("执行");      // 执行:非空字符串是 truthy
if ([])  console.log("执行");      // 执行:空数组是 truthy
if ({})  console.log("执行");      // 执行:空对象是 truthy
if (" ") console.log("执行");      // 执行:空格不是空字符串

判断「用户有没有输入」时不要写 if (str),因为 "0" 会误判为空。明确判断:if (str.length > 0)。

算术运算符的隐式转换

"3" - 1      // 2   减号只做数值运算,字符串被转成数字
"3" + 1      // "31" 加号遇到字符串优先拼接
3 + true     // 4   true 转成 1
"6" * "2"    // 12  乘除取模都会转数字
10 / "a"     // NaN

NaN 的特殊性

NaN(Not a Number)表示「算出来不是合法数字」,它不等于任何值,包括自己:

NaN === NaN   // false
Number.isNaN(NaN)      // true  推荐
isNaN("abc")           // true  会先转类型,容易误判
Number.isNaN("abc")    // false 只有真正的 NaN 才为 true

检测 NaN 用 Number.isNaN()。

逻辑运算符的真实返回值

&& 和 || 不返回布尔值,它们返回决定结果的那个操作数:

const a = 0 || "默认";     // "默认"(0 是 falsy,取右边)
const b = "有值" || "默认"; // "有值"
const c = null && "不执行"; // null(左边 falsy,直接返回左边)

?? 与 || 的区别(重要)

两者都用来兜底,但判断条件不同:

const count = 0;
count || 10    // 10   0 是 falsy,被替换掉了
count ?? 10    // 0    0 是有效值,保留

const text = "";
text || "匿名"  // "匿名"
text ?? "匿名"  // ""   空字符串是有效值,保留

??(空值合并)只在值为 null 或 undefined 时兜底;|| 在所有 falsy 值上兜底。

配置读取、默认值填充应该用 ??,避免把 0、""、false 这些合法值误替换掉。

function greet(name) {
  const who = name ?? "匿名";
  console.log(`你好,${who}`);
}

注意:?? 不能和 ||、&& 直接混用,必须加括号:

a ?? b || c      // SyntaxError
(a ?? b) || c    // 正确

可选链 ?. 与空值赋值 ??=

访问深层属性时,中间任何一层是 null / undefined 都会抛错:

const city = user.address.city;      // address 为空时抛 TypeError
const city = user?.address?.city;    // 安全,短路返回 undefined

??= 表示「仅当左侧为 null / undefined 时赋值」:

const config = { retries: 0 };
config.retries ??= 3;     // 0 是有效值,不覆盖
config.timeout ??= 5000;  // undefined,赋值为 5000
console.log(config);      // { retries: 0, timeout: 5000 }

显式类型转换

依赖隐式转换是可读性杀手,需要转换时写清楚:

Number("42")      // 42
Number("42abc")   // NaN(parseInt 会得到 42,注意区别)
parseInt("42px", 10)  // 42,忽略尾部非数字字符,务必传第二个参数 10
String(42)        // "42"
Boolean("")       // false

常见错误与注意事项

  1. 用 == 比较导致 "" == 0 成立:改用 ===。
  2. if (value) 判断数字是否为 0:0 是 falsy,会被跳过。判断存在性用 value !== undefined。
  3. 把 NaN 当 falsy 处理并兜底:NaN 确实是 falsy,但它往往说明上游计算出错,应该排查而不是给默认值。
  4. || 覆盖了 0 和 "":改成 ??。
  5. 链式访问深层属性不判空:用 ?.,但仍要处理最终是 undefined 的情况。

小结

  • 比较用 ===,兜底用 ??,深层访问用 ?.。
  • 只有 8 个 falsy 值;[]、{}、"0"、" " 都是 truthy。
  • && / || 返回操作数本身,不是布尔值。
  • 检测 NaN 用 Number.isNaN()。

下一步