学习 › JavaScript
各语言特有 · 41
JavaScript 的相等与真假
== 与 ===、假值、?? 与 ||、全是 double 的数字、undefined 与 null,以及 TypeScript 能拦住的问题。
本单元适用于 JavaScript · TypeScript。
其他语言比较时不会偷偷转换类型。
Python 的 "5" == 5 只是 False(比较大小 "5" < 5 会报 TypeError),Java 和 C# 直接编译报错、必须自己转换,Craykon 的 == 遇到不同类型也是假。
JavaScript 在比较或判断真假时会悄悄转换类型。
下面的示例用输入读到的文字 "5" 和 "0",展示 == 与 ===、|| 与 ??、浮点运算、undefined 与 null 会怎样给出不同的结果。
const [a, b] = require("fs").readFileSync(0, "utf8").trim().split("\n"); console.log(a == 5, a === 5, a === "5");console.log(a + 1, Number(a) + 1);console.log(0 == "", null == undefined, null === undefined); const falsy = [0, "", NaN, null, undefined, false];console.log(falsy.filter(Boolean).length, Boolean("0"), Boolean([])); const count = Number(b);console.log(count || 10, count ?? 10); console.log(0.1 + 0.2, 0.1 + 0.2 === 0.3);console.log(Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON);console.log(Number.MAX_SAFE_INTEGER + 2);console.log(String(2n ** 64n)); const nan = Number("abc");console.log(nan === nan, Number.isNaN(nan)); const user = { name: "Kim" };console.log(user.age, typeof null, user.age ?? "none");import { readFileSync } from "node:fs"; const [a, b]: string[] = readFileSync(0, "utf8").trim().split("\n"); // a == 5는 컴파일 오류 TS2367(string과 number는 같을 수 없어요). any로 검사를 끄면 JS와 똑같이 돌아요.const raw: any = a;const zero: any = 0;console.log(raw == 5, raw === 5, a === "5");console.log(a + 1, Number(a) + 1);console.log(zero == "", null == undefined, null === undefined); const falsy: unknown[] = [0, "", NaN, null, undefined, false];console.log(falsy.filter(Boolean).length, Boolean("0"), Boolean([])); const count: number = Number(b);console.log(count || 10, count ?? 10); console.log(0.1 + 0.2, 0.1 + 0.2 === 0.3);console.log(Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON);console.log(Number.MAX_SAFE_INTEGER + 2);console.log(String(2n ** 64n)); const nan: number = Number("abc");console.log(nan === nan, Number.isNaN(nan)); // user.age는 타입에 없으면 컴파일 오류 TS2339라서 age?: number로 적어 둬요const user: { name: string; age?: number } = { name: "Kim" };console.log(user.age, typeof null, user.age ?? "none");5 0
true false true 51 6 true true false 0 true true 10 0 0.30000000000000004 false true 9007199254740992 18446744073709551616 false true undefined object none
要点
==在类型不同时会先转换一边再比较。
所以"5" == 5、0 == ""、null == undefined都是true。===要求类型也相同。
转换规则很复杂,与其去记,不如一直用===和!==。+只要有一边是文字就会拼接。"5" + 1是"51",而"5" - 1是4。
输入总是文字,先用Number(...)转换。- 假值(falsy)只有
false、0、-0、0n、""、null、undefined、NaN。"0"和空数组[]都是真(Python 里[]是假)。 - “没有值”有两种。
undefined表示从没放过值(不存在的属性、没传的参数、没有return的函数的返回值);null是有人故意放的“空”。
两种一起判断时用x == null或x ?? 默认值。 NaN连自己都不相等,NaN === NaN是false,要用Number.isNaN(x)判断。typeof null是"object",是早期 JavaScript 留下的错误。
各语言的写法
| JavaScript | 比较用 ===、!==,默认值用 ??,文字用 Number(...) 转成数字。这些陷阱在运行时不会有任何警告 |
|---|---|
| TypeScript | 能在编译时拦住一部分:a == 5(string 和 number)是 TS2367,x === NaN 是 TS2845,类型里没有的 user.age 是 TS2339。strict 下使用 age?: number 之前必须先检查是否为 undefined。但类型在运行前就被擦除,运行时就是原样的 JavaScript,用 any 或 as 会关掉检查(示例中的 raw) |
常见错误
用 || 给默认值
const count = 0;
console.log(count || 10, count ?? 10);→ 10 0 —— 左边是假值时 || 就给右边,有效值 0 变成了 10
用 ??,它只在 null、undefined 时才给右边。
用 === 比较小数
console.log(0.1 + 0.2);
console.log(0.1 + 0.2 === 0.3);→ 0.30000000000000004、false —— 所有数字都是同一种 64 位浮点数(double),会有误差
比较差值,如 Math.abs(x - y) < Number.EPSILON。
直接计算非常大的整数
console.log(Number.MAX_SAFE_INTEGER + 2);→ 9007199254740992 —— 整数只在 2^53 − 1(9007199254740991)以内精确,所以得不到 ...993
用带 n 后缀的 BigInt 计算,如 2n ** 64n。
像 1n + 1 这样和普通数字混用会报 TypeError。
各语言的差异
比较用
比较用
===,默认值用 ??。== 和 || 会不报错地给出不同的答案,比如 "5" == 5 为真、0 变成 10。