{ }codelper

学习 › TypeScript

各语言特有 · 41

JavaScript 的相等与真假

== 与 ===、假值、?? 与 ||、全是 double 的数字、undefined 与 null,以及 TypeScript 能拦住的问题。

本单元适用于 JavaScript · TypeScript。
其他语言比较时不会偷偷转换类型。
Python 的 "5" == 5 只是 False(比较大小 "5" < 5 会报 TypeError),Java 和 C# 直接编译报错、必须自己转换,Craykon 的 == 遇到不同类型也是假。

JavaScript 在比较或判断真假时会悄悄转换类型。
下面的示例用输入读到的文字 "5" 和 "0",展示 == 与 ===、|| 与 ??、浮点运算、undefined 与 null 会怎样给出不同的结果。

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");
stdin
5
0
stdout
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。

练习题