JavaScriptの「&&」「||」について盛大に勘違いをしていた件

More than 1 year has passed since last update.

論理演算子「&&」「||」について

JavaScriptの基本である論理演算子の && || について、
根本的に勘違いをしていたことに最近気付いたので自戒の意味を込めてここに記します。

論理演算子の使い道

1. お馴染みの使い道「条件処理」

JavaScriptには皆さんご存知の通り論理演算子&& ||が存在します。
それぞれ「AND」「OR」という意味で、条件処理の中で使うことが多い演算子です。

increment.js
// aとbに0または1を足し続ける
// aとbのどちらかが最大値に達すると終了
var a = 0, b = 0, max = 50;

// 条件式その1 AND
while (a < max && b < max) {
    // 0または1を足す
    a += Math.round(Math.random());
    b += Math.round(Math.random());
    console.log('a = ' + a + ', b = ' + b);

    // 条件式その2 OR
    if (a === max || b === max) {
        console.log('一部の変数が最大値に達したため処理を終了します。');
    }
}

2. イディオムっぽい使い道「条件分岐の省略」

論理演算子はしばしば 条件分岐の省略 の為に使われます。
論理演算子||の左側がtrueと評価出来る場合は左側の値、
そうでなければ右側の値を返します。

以下はその例です。

example.js
// 変数 name の値がtrueと評価されるならばname, 
// そうでなければ文字列'anonymous'が変数playerにセットされる

// 条件分岐省略前
var player;

if (name) {
    player = name;
} else{
    player = 'anonymous';
}

// 条件分岐省略後
var player = name || 'anonymous';

何この用法、イミワカンナイ。。

この「条件分岐の省略」用法については書籍で紹介されていたり、JavaScriptのテクニックの一つとして紹介しているサイトを見かけたりしますが、
何故こうなるのか?
この特殊な用法の呼び名は何というのか?
どのような条件でこの用法が使えるのか?
という点についてがわからず、長いこと腑に落ちませんでした。

しかし最近になってようやく自分がとんだ勘違い野郎だったと理解できたので書いていきたいと思います。

「条件分岐の省略」用法の正体

「論理演算子 = 必ずBooleanの値を返す」という先入観

他の言語の影響から、論理演算子は必ずBooleanの値を返すという先入観があり、
そのせいでこの用法が特殊だと勘違いしていました。

しかしJavaScriptの仕様となる ECMAScript には以下のようにあります。

The value produced by a && or || operator is not necessarily of type Boolean.
The value produced will always be the value of one of the two operand expressions.

[ざっくり訳]
&&演算子、||演算子によって生成された値は必ずしもBoolean型である必要はありません。
その値は必ず2つのオペランドのうちの1つとなります。

よって論理演算子 && ||何らかのルールに沿って演算子の両側の値のうちの一方を返す演算子であるということになります。

論理演算子は何を返すのか

MDNの論理演算子のページ によると、論理演算子の返す値は以下の通りです。

【expr1 && expr2】
・expr1 を false と見ることができる場合は、expr1 を返します。
・そうでない場合は、expr2 を返します。
・したがって、真偽値と共に使われた場合、 演算対象の両方が true ならば、&& は、true を返し、
 そうでなければ、false を返します。
【expr1 || expr2】
・expr1 を true と見ることができる場合は、expr1 を返します。
・そうでない場合は、expr2 を返します。
・したがって、真偽値と共に使われた場合、 演算対象のどちらかが true ならば、|| は、true を返し、
 両方とも false の場合は、false を返します。

前述した「条件分岐の省略」の例を、上記の【expr1 || expr2】に当てはめると以下のようになります。

・name を true と見ることができる場合は、name を返します。
・そうでない場合は、'anonymous' を返します。

以上より「条件分岐の省略」用法は特殊な用法ではなく、
論理演算子の本来の挙動を利用していたということがわかりました。

これにて1年以上は悩まされ続けてきた疑問から解放されました。。

おまけ1:falseと見ることができる値

余談ですが、文中で度々出てきたfalseと見ることができる値は、JavaScriptでは以下の7つになります。

・0
・-0
・null
・false
・NaN
・undefined
・空文字列 ("")

よってtrueと見ることができる値は、上記以外のすべての値を指します。

おまけ2:&&を使った「条件分岐の省略」の例

&&||と比べて「条件分岐の省略」の手段として登場する機会は少ないと思いますが、
underscore.jsのソースコード内でグローバルオブジェクトの取得時に&&||を併用した条件分岐の省略が行われているのを見かけました。
&&の使用例としてとてもわかりやすかったのでご紹介します。

underscore.js
 var root = typeof self == 'object' && self.self === self && self ||
            typeof global == 'object' && global.global === global && global ||
            this;

ちなみに演算子の優先順位は&& > ||で、左から右に結合していきます。
最終的には self global this のどれかが変数 root に代入されます。
以下は上記のコードで条件分岐を省略しなかった場合の例です。

get_global_object.js
var root;
if (typeof self == 'object' && self.self === self) {
    root = self;
} else if (typeof global == 'object' && global.global === global) {
    root = global;
} else {
    root = this;
}