2021年12月15日

論理演算子

JavaScriptには4つの論理演算子があります: || (OR:論理和), && (AND:論理積), ! (NOT:否定), ?? (Null合体)。ここでは最初の3つを説明し、?? 演算子は次の記事で説明します。

これらは “論理” と呼ばれますが、Boolean 型だけでなく、どの型の値にも適用することができます。結果もまた任意の型になります。

では、詳細を見ていきましょう。

|| (OR)

“OR” 演算子は2つの縦の記号で表現されます:

result = a || b;

古典的なプログラミングでは、論理和は真偽値のみを操作することを意味していました。もしもその引数のいずれかが true の場合、それは true を返します。そうでなければ false を返します。

JavaScriptでは、演算子は少し難解ですが強力です。最初に真偽値で起こることを見てみましょう。

4つの取りうる論理的な組み合わせがあります:

alert( true || true );   // true
alert( false || true );  // true
alert( true || false );  // true
alert( false || false ); // false

ご覧の通り、両方のオペランドが false の場合を除き、結果は常に true です。

もしもオペランドが Boolean でない場合、評価のために Boolean に変換されます。

例えば、数値 1true として扱われ、数値 0false となります:

if (1 || 0) { // if( true || false ) のように動作します
  alert( 'truthy!' );
}

ほとんどの場合、OR ||if 文の中で、与えられた条件のいずれかが正しいかを確認するのに使われます。

例:

let hour = 9;

if (hour < 10 || hour > 18) {
  alert( 'The office is closed.' );
}

より多くの条件を書くこともできます:

let hour = 12;
let isWeekend = true;

if (hour < 10 || hour > 18 || isWeekend) {
  alert( 'The office is closed.' ); // 週末です
}

OR は最初の真値を探します

上で描かれたロジックはいくらか古典的です。ここで JavaScriptの特別な機能を持ってきましょう。

拡張されたアルゴリズムは次の通りに動作します。

与えられた複数の OR の値:

result = value1 || value2 || value3;

OR "||" 演算子は次のように動きます:

  • 左から右にオペランドを評価します。
  • それぞれのオペランドで、それを Boolean に変換します。もしも結果が true であれば、停止しオペランドの本来の値を返します。
  • もしもすべての他のオペランドが評価された場合(i.e. すべて のとき), 最後のオペランドを返します。

値は変換されていない元の形式で返却されます。

つまり、OR "||" のチェーンは最初に真となる値を返し、そのような値がない場合には最後のオペランドが返却されます。

例:

alert( 1 || 0 ); // 1 (1 は真)

alert( null || 1 ); // 1 (1 は最初の真値)
alert( null || 0 || 1 ); // 1 (最初の真値)

alert( undefined || null || 0 ); // 0 (すべて偽、なので最後の値が返却される)

この結果は、“純粋で昔ながらの真偽値のみの OR” と比較して、いくつかの興味深い使用方法につながります。

  1. 変数または式のリストから最初の真値を取得する

    例えば、firstName, lastNamenickName 変数があり、すべて任意( undefined あるいは偽となる値になりうる)とします。

    データを持っているものを選び、表示する(あるいは何も設定されていな場合は "Anonymous")のに、OR || が利用できます:

    let firstName = "";
    let lastName = "";
    let nickName = "SuperCoder";
    
    alert( firstName || lastName || nickName || "Anonymous"); // SuperCoder

    すべての変数が偽であれば、"Anonymous" が表示されます。

  2. 短絡評価(最小評価)

    OR || のもう1つの特徴はいわゆる “短絡” 評価と呼ばれます。

    つまり、|| は最初の真値に到達するまで引数を処理し、その後は他の引数には触れることなく、値はすぐに返却されることを意味します。

    この機能の重要性は、オペランドが単なる値ではなく、変数の割当や関数呼び出しなどの副作用のある式である場合に明らかになります。

    以下の例を実行した場合、2つ目のメッセージだけが表示されます:

    true || alert("not printed");
    false || alert("printed");

    1行目では、OR || 演算子が true を見るとすぐに評価を停止するため、alert は実行されません。

    条件の左側が false のときにだけコマンドを実行するためにこの特徴を利用する人もいます。

&& (AND)

AND 演算子は2つのアンパサンド && で表されます:

result = a && b;

古典的なプログラミングでは、AND は両方のオペランドが真のときに true を返します。それ以外の場合は false です:

alert( true && true );   // true
alert( false && true );  // false
alert( true && false );  // false
alert( false && false ); // false

if の例:

let hour = 12;
let minute = 30;

if (hour == 12 && minute == 30) {
  alert( 'Time is 12:30' );
}

OR のように、AND のオペランドとして任意の値が許可されています:

if (1 && 0) { // true && false として評価される
  alert( "won't work, because the result is falsy" );
}

AND は最初の偽値を探します

複数のANDされた値が与えられました:

result = value1 && value2 && value3;

AND "&&" 演算子は次のように動きます:

  • 左から右にオペランドを評価します。
  • それぞれのオペランドで、それを Boolean に変換します。もしも結果が false の場合、ストップしそのオペランドの本来の値を返します。
  • もしもすべての他のオペランドが評価された場合(i.e. すべて のとき), 最後のオペランドを返します。

つまり、ANDは最初の偽値、またはない場合には最後の値を返します。

上のルールはORと似ています。違いはANDは最初の 偽値 でORは最初の 真値 です。

例:

// 最初のオペランドが真の場合、
// AND は2つ目のオペランドを返す:
alert( 1 && 0 ); // 0
alert( 1 && 5 ); // 5

// 最初のオペランドが偽の場合、
// AND はそれを返します。2つ目のオペランドは無視されます。
alert( null && 5 ); // null
alert( 0 && "no matter what" ); // 0

より多くの値を渡すこともできます。どのように最初の偽値が返却されるか見てください。:

alert( 1 && 2 && null && 3 ); // null

すべての値が真のとき、最後の値が返却されます。:

alert( 1 && 2 && 3 ); // 3, 最後のオペランド
AND && は OR || の前に実行します

AND && 演算子の優先順位は OR || よりも高いです。

そのため、コード a && b || c && d&& 式が括弧の中にある場合 (a && b) || (c && d) と本質的に同じです。

if||&& に置き換えないでください

時々、AND && 演算子を “ifを短く書く方法” として利用する人がいます。

例:

let x = 1;

(x > 0) && alert( 'Greater than zero!' );

&& の右側のアクションは、その評価に到達した場合にのみ実行されます。つまり: (x > 0) が true の場合のみです。

なので、基本的に同じことをする別の方法があります:

let x = 1;

if (x > 0) alert( 'Greater than zero!' );

&& を含むやり方は、より短いように見えますが、if はより明白で、読みやすい傾向にあります。そのため、すべての構文をその目的に合わせて使うことを推奨します。条件判定が必要なら if を、論理積が必要なら && を使います。

! (NOT)

真偽値否定演算子は感嘆符 "!" で表現されます。

構文はとてもシンプルです:

result = !value;

演算子は1つの引数を取り、次のようにします:

  1. オペランドを真偽値型に変換します: true/false
  2. 逆の値を返します。

例:

alert( !true ); // false
alert( !0 ); // true

2つの否定 !! は値を真偽値型に変換するために使われることがあります:

alert( !!"non-empty string" ); // true
alert( !!null ); // false

つまり、最初の NOT は値を真偽値に変換しその逆を返します。そして、2つ目の NOT は再びその逆をします。最終的に、明示的な値からブール値への変換を行います。

少し冗長ですが同じことをする方法があります – 組み込みの Boolean 関数です。:

alert( Boolean("non-empty string") ); // true
alert( Boolean(null) ); // false

NOT ! の優先順はすべての論理演算子でもっとも高いので、&&|| よりも常に最初に実行されます。

タスク

重要性: 5

下のコードは何を出力するでしょう?

alert( null || 2 || undefined );

答えは 2 で、それが最初の真となる値です。

重要性: 3

下のコードは何を出力するでしょう?

alert( alert(1) || 2 || alert(3) );

答え: 最初は 1, 次に 2.

alert( alert(1) || 2 || alert(3) );

alert の呼び出しは値を返しません。また、言い換えると、 undefined を返します。

  1. 最初の OR || はその左のオペランド alert(1) を検査します。それは 1 の最初のメッセージを表示します。
  2. alertundefined を返すので、OR は真値を探すのに2つ目のオペランドに行きます。
  3. 2つ目のペランド 2 は真値なので、実行が中止され 2 が返却されます。次に外部の alert でそれが表示されます。

検査は alert(3) に到達しないので、 3 は現れません。

重要性: 5

このコードは何を表示するでしょう?

alert( 1 && null && 2 );

答え: null です。なぜなら、それがリストの中の最初の偽値だからです。

alert( 1 && null && 2 );
重要性: 3

下のコードは何を表示するでしょう?

alert( alert(1) && alert(2) );

答え: 1, 次に undefined

alert( alert(1) && alert(2) );

alert の呼び出しは undefined を返します(メッセージを表示するだけなので、意味のある返却はありません)。

そのため、 && が左のオペランドを検査(1 を出力)し、すぐに停止します。なぜなら、undefined は偽値だからです。そして && は偽値を探し、それを返します。

重要性: 5

結果はどうなるでしょう?

alert( null || 2 && 3 || 4 );

答え: 3.

alert( null || 2 && 3 || 4 );

AND && の優先順位は || よりも高いので、最初に実行されます。

2 && 3 = 3 なので、式はこのようになります:

null || 3 || 4

これの最初の真値の結果なので、3 です。

重要性: 3

包括的に age1490 の間かをチェックする if 条件を書きなさい。

“包括的に” は age14 または 90 の端に到達できることを意味します。

if (age >= 14 && age <= 90)
重要性: 3

包括的に age が 14 と 90 間ではないことをチェックするための if 条件を書きなさい。

2つのバリアントを作ってください: 最初は NOT ! を使い、2つ目は – それなしです。

最初のバリアント:

if (!(age >= 14 && age <= 90))

2つ目のバリアント:

if (age < 14 || age > 90)
重要性: 5

これらの alert で実行されるのはどれでしょう?

if(...) の内側の式の結果はどうなるでしょう?

if (-1 || 0) alert( 'first' );
if (-1 && 0) alert( 'second' );
if (null || -1 && 1) alert( 'third' );

答え: 1つ目と3つ目が実行されます。

詳細:

// 実行します
// -1 || 0 = -1 なので真です
if (-1 || 0) alert( 'first' );

// 実行されません
// -1 && 0 = 0, 偽です
if (-1 && 0) alert( 'second' );

// 実行します
// 演算子 && は || よりも高い優先順位を持っています
// なので -1 && 1 が最初に実行されます:
// null || -1 && 1  ->  null || 1  ->  1
if (null || -1 && 1) alert( 'third' );
重要性: 3

prompt でログインを要求するコードを書いてください。

もし訪問者が "Admin" と入力したら、パスワードのための prompt を出します。もし入力が空行または Esc の場合 – “Canceled” と表示します。別の文字列の場合は – “I don’t know you” と表示します。

パスワードは次に沿ってチェックされます:

  • ”TheMaster" と等しい場合には “Welcome!” と表示します。
  • 別の文字列の場合 – “Wrong password” を表示します。
  • 空文字または入力がキャンセルされた場合には “Canceled.” と表示します。

図:

入れ子の if ブロックを使ってください。コードの全体的な読みやすさに気をつけてください。

デモを実行

let userName = prompt("Who's there?", '');

if (userName === 'Admin') {

  let pass = prompt('Password?', '');

  if (pass === 'TheMaster') {
    alert( 'Welcome!' );
  } else if (pass === '' || pass === null) {
    alert( 'Canceled' );
  } else {
    alert( 'Wrong password' );
  }

} else if (userName === '' || userName === null) {
  alert( 'Canceled' );
} else {
  alert( "I don't know you" );
}

if ブロック内の縦のインデントに注意してください。技術的には必須ではありませんが、コードの可読性をより良くします。

チュートリアルマップ