ウェブサイト検索

javascript - ??(ヌリッシュ合体演算子)と ||(論理OR)はいつ使うべきですか?


JavaScript に「null 合体」演算子はありますか? - JavaScript には現在 ?? 演算子があり、以前よりも頻繁に使用されているのを見かけます。以前はほとんどの JavaScript コードが || を使用していました。

let userAge = null

// These values will be the same. 
let age1 = userAge || 21
let age2 = userAge ?? 21

?? と || が異なる動作をするのはどのような状況ですか?

回答 #1

OR演算子 || は左側が偽値の場合に右側の値を使用しますが、ヌリッシュ結合演算子 ?? は左側が null または undefined の場合に右側の値を使用します。

これらの演算子は、最初の値が存在しない場合にデフォルト値を提供するためによく使用されます。

しかし、OR 演算子 || は、左側の値に "" や 0、false が含まれる可能性がある場合は問題になることがあります(これらは偽値だからです):

console.log(12 || "not found") // 12
console.log(0  || "not found") // "not found"

console.log("jane" || "not found") // "jane"
console.log(""     || "not found") // "not found"

console.log(true  || "not found") // true
console.log(false || "not found") // "not found"

console.log(undefined || "not found") // "not found"
console.log(null      || "not found") // "not found"

多くの場合、左側が null または undefined の場合にのみ右側の値を欲しいことがあります。これが null 合体演算子 ?? の目的です:

console.log(12 ?? "not found") // 12
console.log(0  ?? "not found") // 0

console.log("jane" ?? "not found") // "jane"
console.log(""     ?? "not found") // ""

console.log(true  ?? "not found") // true
console.log(false ?? "not found") // false

console.log(undefined ?? "not found") // "not found"
console.log(null      ?? "not found") // "not found"

現在のLTSバージョンのNode(v10およびv12)では??演算子は利用できませんが、TypeScriptやNodeの一部のバージョンでは使用できます:

?? 演算子は、2019年11月に TypeScript 3.7 に追加されました。

そして最近、?? 演算子が ES2020 に含まれました。これは Node 14(2020年4月にリリース)でサポートされています。

null合体演算子??がサポートされている場合、通常はOR演算子||の代わりにそれを使用します(特に理由がない限り)。

回答 #2

要するに

Nullish Coalescing 演算子 ?? は次の違いを区別します:

  • nullish 値(null、undefined)
  • 偽だが定義されている値(false、0、''など)

||(論理 OR)は、これらの両方を同じように扱います。

JavaScriptにおけるnullish値とfalsey値の関係を示すために、簡単なグラフィックを作成しました:

さらなる説明:

let x, y

x = 0
y = x || 'default'            // y = 'default'
y = x ?? 'default'            // y = 0

上で見たように、演算子??と||の違いは、一方がnullishな値をチェックし、もう一方がfalseyな値をチェックするという点です。しかし、多くの場合においてそれらは同じように動作します。これは、JavaScriptではすべてのnullishな値がfalseyでもあるためです(ただし、すべてのfalseyな値がnullishであるわけではありません)。

上で学んだことを使って、さまざまな動作のためにいくつかの例を作ることができます:

let y

y = false || 'default'       // y = 'default'
y = false ?? 'default'       // y = false

y = 0n || 'default'          // y = 'default'
y = 0n ?? 'default'          // y = 0n

y = NaN || 'default'         // y = 'default'
y = NaN ?? 'default'         // y = NaN

y = '' || 'default'          // y = 'default'
y = '' ?? 'default'          // y = ''

新しいヌリッシュ合体演算子は、値がない場合と偽の値を区別できるため、例えば文字列がない場合や空の文字列があるかどうかを確認する必要がある場合に便利です。一般的に、ほとんどの場合は || の代わりに ?? を使用したいでしょう。

最後にして最も重要でないのは、彼らが同じように振る舞う二つの例です:

let y

y = null || 'default'        // y = 'default'
y = null ?? 'default'        // y = 'default'

y = undefined || 'default'   // y = 'default'
y = undefined ?? 'default'   // y = 'default'

回答 #3

非常に簡単なルールとして、逆の見方をすることもできます:

  • ||(または)最初の「真」の値を返す(または「真」の値が存在しない場合は最後の値を返す)
  • ??(ヌリッシュ合体演算子)は 最初の「定義された」値を返します(「定義された」値が存在しない場合は最後の値を返します)

例

x = false || true; // -->  true   (the first 'truthy' value - parameter 2)
x = false ?? true; // -->  false  (the first 'defined' value - parameter 1)

回答 #4

MDNのドキュメントに基づいて:

論理OR(||)演算子とは異なり、左オペランドが null または undefined ではない偽値である場合、左オペランドが返されます。

概念的な例:

  • || を undefined や null ではない falsy 値に使用する場合:

    false || 'name'; // ==> the 'name' is returned
    
  • しかし、上記のケースで ?? を使用する場合は:

    false ?? 'name'; // ==> the false is returned
    

実際の例: 例えば、フォームにおいて必須ではない phone 変数があり、空文字列('')も有効で、もし phone 変数が null または undefined の場合に doSomething() を実行したいとします。さて、何が起こると思いますか:

  • || を undefined や null ではない falsy 値に使用する場合:

    const phone = ''; // assume it became empty string from some action
    
    phone || doSomething(); // ==> damn, the doSomething is run 
    // but we want to run it if it's `undefined` or `null` the empty string is valid for us
    
  • しかし、上記のケースで ?? を使用する場合は:

    const phone = ''; // same assumption like above
    
    phone ?? doSomething(); // ==> yeah, that's right
    // the empty string is valid for us and the doSomething is not run
    

注: 実際にはこれは例であり、実際のプロジェクトではこの素晴らしい操作の使い方をよりよく理解できるでしょう。

注意: undefined と null はどちらも互いのように動作します。

回答 #5

MDNに記載されているように:

論理 OR (||) 演算子とは異なり、左側のオペランドが null または undefined でない偽値の場合、その左側のオペランドが返されます。言い換えると、|| を使用して別の変数 foo にデフォルト値を設定する場合、いくつかの偽値(例:'' や 0)を使用可能と見なすと、予期しない動作に遭遇する可能性があります。下記にさらに例があります。

そして、あなたがリンクした質問への答えの中でも:

最初のオペランドの型に関係なく、それをブーリアンにキャストした結果が false になる場合、代入は2番目のオペランドを使用します。以下のすべてのケースに注意してください:

alert(Boolean(null)); // false
alert(Boolean(undefined)); // false
alert(Boolean(0)); // false
alert(Boolean("")); // false
alert(Boolean("false")); // true -- gotcha! :)

回答 #6

|| が false になるブール条件として使用される場合。例えば:

let userAge = false

// These values will be the same. 
let age1 = userAge || 21 // 21
let age2 = userAge ?? 21 // false

論理 OR は true と評価されないものに対しても次の値を返します。したがって、この場合は 21 が返されます。一方で ?? は null と undefined のみを扱います。

回答 #7

function f(input) {
  const val = input || 1;

  return 41 + val;
}

function g(input) {
  const val = input ?? 1;

  return 41 + val;
}

console.log("using ||:", f(0));
console.log("using ??:", g(0));

ヌル(または類似)合体演算子は、null と undefined にのみ 作用 します。したがって、これらの値を避けたい場合にヌル(または類似)合体演算子を使用し、その他の偽値は許容する場合に使用してください:

console.log(null      ?? "nullish");
console.log(undefined ?? "nullish");
console.log(""        ?? "nullish");
console.log(0         ?? "nullish");
console.log(false     ?? "nullish");

論理ORは、偽の値をスキップして他のものを返します。論理ORは、偽の値をスキップして他のパラメータを返します。これは実際に機能し、今では慣用的に使われていますが、必ずしも望む結果とは限りません。

console.log(null      || "falsy");
console.log(undefined || "falsy");
console.log(""        || "falsy");
console.log(0         || "falsy");
console.log(false     || "falsy");

どれが必要かを判断するためのいくつかのルールを示します。最も簡単なテストは次の通りです:

  • null(および undefined - 通常は同じことです)からだけ保護したいのですか?それなら ?? を使ってください。もし確信が持てない場合は、null合体演算子をデフォルトで使うのが良いでしょう。
  • 例えば、0 や "" も望まないことを 知っていますか? それなら || を使ってください。

2つ目は、実際に少し厄介になる部分です。どのようにして偽の値を破棄する必要があるかを知るのでしょうか?さて、最初のスニペットはそれを行った場合に何が起こるかを示しています: f(0)はゼロを破棄してしまい、そのため異なる結果を生成します。これはやや一般的なバグの原因です。a=b || cという構文はフォールバック値(この場合はc)を導入するために一般的に使われるので、意図せずその値にフォールバックしてしまうことがあります。

function updateAge(years) {
  var yearsToAdd = years || 1;
  return this.age + yearsToAdd
}

これは、updateAge()(引数なし)を呼び出すためのフォールバックを提供したい場合には機能しますが、updateAge(0)(更新なし)を呼び出した場合には失敗します。同様に、次のようにすることもできます:

function sayMyName(greeting) {
  var prefix = greeting || "Hello, my name is ";
  return prefix + this.firstName;
}

再び、これは sayMyName()(引数なし)では動作しますが、sayMyName("")(明示的に挨拶なし)では失敗します。

一般化すると、もしあなたが偽の値と 異なる フォールバック値を提供している場合、問題があるかもしれません。しかし、もしフォールバックが 偽の値そのものである 場合 - num || 0 や str || "" のように - それはあまり問題ではありません。

混合型(数値、文字列、オブジェクトなど)を期待してフォールバックを提供することはまれ(あるいはそうあるべき)です:input || fallback は有効ですが、空文字やゼロを拒否してしまいます。通常、これらを明示的に避けたい場合を除いては間違いとなるでしょう。

簡単に言うと、常にヌリッシュ合体演算子 ?? を使用するべきでしょう。それにより、それが潜在的なバグなのかどうかを考える認知的負荷が減ります。また、それを避ける理由もあまりありません。これは論理和(OR)よりも新しい演算子なので、古い環境では動作しないのは事実ですが、現在ではそれらの環境向けにコードをトランスパイルしている可能性が高いでしょう。もしそれができない、あるいは避けたい場合は、選択の余地はなく、論理和を使用するべきです。

関連記事