ウェブエンジニア問題集

演算子と式 — 等価比較・短絡評価・オプショナルチェーン

JavaScriptの演算子には、他の言語とは少し違う挙動をするものがいくつかあります。 特に =====、そして &&|| を使った短絡評価は、実務コードで頻繁に登場するにもかかわらず、誤解されがちなポイントです。

この章では「なぜそう動くのか」を理解することで、コードを書くときに迷わなくなることを目指します。

学習者学習者

=====、結局どっちを使えばいいの?最近見る ???. も何者…?


等価比較:===== の違い

JavaScriptには等価演算子が2種類あります。

console.log(1 == '1'); // true
console.log(1 === '1'); // false

==(抽象的等価比較)

== は比較の前に**型強制(Type Coercion)**を行います。 型が違う場合、JavaScriptが自動的に片方の型を変換してから比較します。

console.log(0 == false); // true  — false が 0 に変換される
console.log('' == false); // true  — どちらも 0 に変換される
console.log(null == undefined); // true  — 特別ルール
console.log(null == 0); // false — null は null/undefined としか == で true にならない
console.log([] == false); // true  — [] → '' → 0、false → 0

型強制のルールは複雑で、人間の直感に反する結果になることが多いです。

===(厳格等価比較)

=== は型強制を行わず、型も値も完全に一致する場合のみ true を返します。

console.log(1 === 1); // true
console.log(1 === '1'); // false — 型が違う
console.log(null === null); // true
console.log(null === undefined); // false — 型が違う

== の比較フローを図で整理します。

実務では == を使う理由はほぼなく、常に === を使うのがベストプラクティスです。唯一の例外は null チェックで、null == undefinedtrue になる性質を意図的に使うケースがあります。

// null と undefined のどちらも弾きたい場合のみ == を使う例外的なパターン
if (value == null) {
  // value が null または undefined のときに入る
}
 
// 上と同じ意味(こちらの方が明示的)
if (value === null || value === undefined) {
  // ...
}

論理演算子と短絡評価

&&(AND)と ||(OR)は単なる真偽値チェックではなく、値をそのまま返すという重要な性質があります。

&&:左辺が falsy なら左辺を、truthy なら右辺を返す

console.log(0 && 'hello'); // 0      — 左辺が falsy なので左辺を返す
console.log(1 && 'hello'); // 'hello' — 左辺が truthy なので右辺を返す
console.log('' && 'hello'); // ''     — 左辺が falsy
console.log('foo' && 'bar'); // 'bar'  — 左辺が truthy なので右辺を返す

右辺まで評価しないことを**短絡評価(short-circuit evaluation)**と呼びます。 左辺が false と確定した時点で、右辺は実行されません。

// 右辺の関数はログが false のとき実行されない
const isLoggedIn = false;
isLoggedIn && console.log('ようこそ'); // 何も出力されない
 
// React でよく見るパターン:条件付きレンダリング
function UserGreeting({ user }) {
  return <div>{user && <span>こんにちは、{user.name}さん</span>}</div>;
}

||:左辺が falsy なら右辺を、truthy なら左辺を返す

console.log(0 || 'デフォルト'); // 'デフォルト' — 左辺が falsy
console.log('hello' || 'デフォルト'); // 'hello'  — 左辺が truthy
console.log(null || 'フォールバック'); // 'フォールバック'

デフォルト値のフォールバックパターンとしてよく使われます。

function greet(name) {
  const displayName = name || '名無し';
  return `こんにちは、${displayName}さん`;
}
 
greet('Alice'); // "こんにちは、Aliceさん"
greet(''); // "こんにちは、名無しさん" — 空文字は falsy
greet(null); // "こんにちは、名無しさん"
考えている男性のイラスト

Nullish Coalescing:??

|| のデフォルト値パターンには落とし穴があります。0'' も falsy として扱われるため、意図しないフォールバックが起きることがあります。

const config = {
  timeout: 0, // タイムアウトなし(意図的な 0)
  label: '', // 空ラベル(意図的な空文字)
};
 
// || を使うと意図しない結果に
const timeout = config.timeout || 5000; // 5000 になってしまう(0 は falsy)
const label = config.label || '未設定'; // '未設定' になってしまう

??(Nullish Coalescing)は null または undefined のときだけフォールバックし、0''falsy でもそのまま左辺を返す演算子です。

console.log(0 ?? 'デフォルト'); // 0     — 0 は nullish ではない
console.log('' ?? 'デフォルト'); // ''    — '' は nullish ではない
console.log(null ?? 'デフォルト'); // 'デフォルト'
console.log(undefined ?? 'デフォルト'); // 'デフォルト'

先ほどの例を ?? で書き直すと意図通りになります。

const timeout = config.timeout ?? 5000; // 0 のまま
const label = config.label ?? '未設定'; // '' のまま

オプショナルチェーン:?.

ネストされたオブジェクトのプロパティにアクセスするとき、途中が nullundefined だとエラーになります。

const user = null;
 
console.log(user.profile.name);
// TypeError: Cannot read properties of null (reading 'profile')

従来は && を使って防御的に書く必要がありました。

const name = user && user.profile && user.profile.name;
// 冗長で読みにくい

?.(オプショナルチェーン)を使うと、途中が null または undefined の場合にエラーを投げず undefined を返すようになります。

const user = null;
console.log(user?.profile?.name); // undefined(エラーにならない)
 
const user2 = { profile: { name: 'Alice' } };
console.log(user2?.profile?.name); // 'Alice'

メソッド呼び出しにも使える

const callback = null;
callback?.(); // エラーにならず undefined を返す
 
const arr = null;
arr?.map((x) => x * 2); // undefined(エラーにならない)

ブラケット記法にも使える

const data = null;
console.log(data?.['key']); // undefined

?? との組み合わせ

?.undefined が返ってきた場合のフォールバックに ?? を組み合わせるパターンは非常によく使われます。

const user = { profile: null };
 
const name = user?.profile?.name ?? '名前未設定';
const country = user?.address?.country ?? '国籍不明';
 
console.log(name); // '名前未設定'
console.log(country); // '国籍不明'
ほっとしている男性のイラスト

論理代入演算子

ES2021 で追加された演算子です。論理演算と代入を組み合わせた省略記法です。

// &&= : 左辺が truthy のときだけ右辺を代入
let a = 1;
a &&= 10; // a は truthy なので 10 が代入される → 10
let b = 0;
b &&= 10; // b は falsy なので代入されない → 0
 
// ||= : 左辺が falsy のときだけ右辺を代入
let c = null;
c ||= 'デフォルト'; // c は falsy なので代入される → 'デフォルト'
let d = 'hello';
d ||= 'デフォルト'; // d は truthy なので代入されない → 'hello'
 
// ??= : 左辺が null/undefined のときだけ右辺を代入
let e = 0;
e ??= 99; // 0 は nullish ではないので代入されない → 0
let f = null;
f ??= 99; // null なので代入される → 99

演算子の優先順位と括弧

&&|| より優先順位が高いです。

// && が先に評価される
true ||
  (false &&
    false(
      // → true || (false && false)
      // → true || false
      // → true
 
      // 意図を明確にするなら括弧を使う
      true || false,
    ) &&
    false); // → false

??&&|| と混在させることができません(優先順位が曖昧なため、構文エラーになります)。

// エラー
null || undefined ?? 'デフォルト'; // SyntaxError
 
// 括弧で明示する
(null || undefined) ?? 'デフォルト'; // 'デフォルト'
null || (undefined ?? 'デフォルト'); // 'デフォルト'

よくある疑問

falsy な値はどれ?

JavaScriptで「偽として扱われる」値(falsy)は6種類だけです。

備考
falseboolean真偽値の false
0numberゼロ(-00n も含む)
''string空文字列
nullnull値がないことを明示
undefinedundefined未定義
NaNnumber非数

それ以外はすべて truthy です。[](空配列)や {}(空オブジェクト)も truthy です。

console.log([] == false); // true  — == の型強制で変換されるから
if ([]) {
  console.log('空配列は truthy'); // こちらが実行される
}
疑問を持っている女性のイラスト

NaN の比較

NaN は自分自身とも等しくないという特殊な値です。

console.log(NaN === NaN); // false — NaN は自分自身とも一致しない
console.log(NaN == NaN); // false
 
// 正しいチェック方法
console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN(undefined)); // false(isNaN() と挙動が異なる)

まとめ

演算子用途注意点
===等価比較(推奨)型も値も比較
==基本使わない型強制が発生
&&条件付き実行・レンダリング左辺が 0 のとき 0 が返る
||falsy フォールバック0'' も上書きされる
??null/undefined フォールバック0'' は上書きされない
?.ネストした null セーフアクセス過度な使用は null を隠す
??=null/undefined のときだけ代入キャッシュ初期化などに便利
ステップアップしている男性のイラスト

次の章では制御構文(ifswitch・ループ)の実務パターンを見ていきます。