{ }codelper

学ぶ › JavaScript

言語ごとの特徴 · 41

JavaScript の等価と真偽

== と ===、falsy な値、?? と ||、すべて 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");
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 では [] は偽)。
  • 「値がない」が2種類あります。
    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)1種類なので誤差が出ます

Math.abs(x - y) < Number.EPSILON のように差で比べます。

とても大きい整数をそのまま計算した

console.log(Number.MAX_SAFE_INTEGER + 2);

→ 9007199254740992 — 整数は 2^53 − 1(9007199254740991)までしか正確でないので、...993 になりません

2n 64n のように n を付けた BigInt** で計算します。
1n + 1 のようにふつうの数と混ぜると TypeError です。

言語ごとの違い
比較は ===、既定値は ?? で書きましょう。
== と || は、"5" == 5 が真になったり 0 が 10 に変わったりと、エラーなしで違う答えを出します。

練習問題