JavaScriptのANDとOR【現場でよく使われる特殊な書き方】

2019年7月17日水曜日

プログラミング

JavaScriptのANDとOR【現場でよく使われる特殊な書き方】

こんにちは、財前です。


AND条件やOR条件は、基本的にどんなプログラミング言語をやっても出てくるものですね。

JavaScriptでは「&&」がAND条件、
||」がOR条件を意味します。

真偽値(trueとfalse)を扱うときに用いる、すごく基本的な演算子です。

ただ、現場でJavaScriptのソースコードを追っていると、ANDやORのちょっと特殊な使い方をしているプログラムをよく見るかも知れません。




たとえばこんなやつ…

if(name && name.length > 20){
  errMsg = “名前は20文字以内で入力してちょ”;
}

name.lengthを見ていますし、おそらくここでnameには文字列が入っているのでしょう。
ということは
「name &&」の部分は、
「文字列 &&」ってこと!?

trueやfalseじゃなくていいの!?




ということや…



もしくはこんなやつ…
const message = errMsg || “正常っす”;



OR条件の後に文字列が来てる!?
messageには何が入るの!?


などなど…




上記はJavaScriptの論理演算子の特殊な性質を使った、ちょっとだけトリッキーな書き方ですが、仕事でソースコードを読んでいると、とても頻繁に出てきます。

そんなソースコードに出会った時のために、JavaScriptのAND条件とOR条件の特殊な使い方と、その仕組みを見ていきましょう。


一般的なAND条件とOR条件

一般的なAND条件とOR条件

前述のとおり、AND条件やOR条件は、基本的には真偽値を扱うときに出てくる、論理演算子と呼ばれるものです。

JavaScript以外の言語も含めて、以下のような性質がありますね。


A and B
  • AとBの両方がtrueだった時のみtrue。
  • それ以外はfalse。


A or B
  • AとBの両方がfalseだった時のみfalse。
  • それ以外はtrue。



ここまでは、少しでもプログラミングをやったことがある方であれば、
「当たり前じゃん」
という感じかも知れません。


ただ、ここで気にして欲しいのは、以下の3つの疑問点です。


  • もしAやBが、真偽値(trueやfalse)じゃなかったらどうするの?
  • Aを見ただけで結果が確定する場合、わざわざ次のBの中身まで確認するの?
  • 結果は必ず真偽値(trueやfalse)?




JavaScriptの論理演算子の特徴

JavaScriptの論理演算演算子の特徴

上記の3つの疑問点の答えは、
プログラミング言語によって異なります。


JavaScriptの場合は以下のような答えになります。


  • もしAやBが、真偽値(trueかfalse)じゃなかったらどうするの?
  → 自動で真偽値に変換して評価する。


  • Aを見ただけで結果が確定する場合、わざわざ次のBの中身まで確認するの?
  → しない。確定した時点で評価を打ち切る。


  • 結果は必ず真偽値(trueやfalse)?
  → 結果が真偽値とは限らない。最後に評価された値がそのまま結果になる。



この3つの性質を応用すると、この記事の最初で確認したような書き方になるのです…

具体例の前に、この3つの性質の詳細を見てみましょう。


評価対象が真偽値じゃなかった場合


JavaScriptでは、
「A && B」の
AやBが真偽値ではなかった場合も、
無理矢理に評価を行います。


たとえば、
console.log(“こんにちは” && true);
  → true

console.log(0 || false);
  → false



上記の例で “こんにちは” は文字列ですし、
0は数値です。

どちらも真偽値ではありませんが、特にエラーなどが起きることなく、処理が行われます。


なぜ問題なく評価が行われるのかというと、JavaScriptでは、真偽値でない値に対して論理演算を行おうとすると、以下のような変換が自動で行われるからです。



  • 空文字列(“”)、0、-0、NaN、null、undefined
  → falseとみなす


  • 上記以外のもの
  → trueとみなす



たとえtrueやfalseじゃない値でも、自動で真偽値とみなして評価してくれるので、エラーなどは起きないのですね。

変換ルールの覚え方としては、falseとみなされるものだけ覚えておけば良さそうです。

基本的には値があればtrueで、空っぽのものがfalseになるような感じですね。


ただ、オブジェクトは上記のfalseとみなされるものの一覧に入っていませんから、問答無用でtrueになります。

プロパティを持たないオブジェクト{}や、要素を持たない配列[]も、trueとなることは注意した方が良いかも知れませんね。

空っぽに見えますが、trueです。



結果が確定した時点で処理をやめる

JavaScriptは結果が確定した時点で処理をやめる

JavaScriptの論理演算では、1つ目の値を見て結果が確定する場合は、2つ目の値は評価されません。

どういうことかというと、たとえば、
const a = true;
const b = false;
console.log(a || b);

という処理を行ったとき、
当然結果はtrueなのですが、
「a || b」の結果は、最初のaがtrueだった時点でtrueに確定しますね。

OR条件では、どちらかがtrueであれば結果もtrueになりますから。
bの値に関係なく、結果はtrueになるはずです。

この時、JavaScriptでは、不要なbの値の確認を行いません。
時間の無駄なので、評価を途中で打ち切ります。



具体的には、以下の2つのパターンの時に、評価を途中で打ち切ります。

「&&」演算子の前の値がfalseだったとき
 (結果がfalseに確定するため)

「||」演算子の前の値がtrueだったとき
 (結果がtrueに確定するため)



このことを利用したのが、冒頭で確認したプログラムの例です。
if(name && name.length > 20){
  errMsg = “名前は20文字以内で入力してちょ”;
}

この例では、nameという変数のlengthプロパティを確認し、20よりも大きければエラーメッセージを設定しているようです。

変数nameに格納された文字列の長さを見ていますね。


つまり、やりたいことだけをそのまま書くと、以下のようなプログラムになりそうです。
if(name.length > 20){
  errMsg = “名前は20文字以内で入力してちょ”;
}

nameの長さが20より大きければ、エラーメッセージを格納していますから、何だかこれだけで事足りそうです。


ただ、この書き方はよろしくないとされています。

これはJavaScriptに限らないのですが、オブジェクトのプロパティを参照する時には、
そのオブジェクト自体がnullやundefinedである可能性を常に考慮する必要があります。

(厳密に言うとJavaScriptの文字列は、オブジェクトではなくプリミティブ値と呼ばれるものですが、本題とあまり関係がない、かつ混乱を招くため、ここでの説明は割愛します。)


つまりどういうことかというと、以下のような書き方をした時に、エラーが発生し、プログラムが異常終了してしまうのです。
let name;

if(name.length > 20){
  errMsg = “名前は20文字以内で入力してちょ”;
}

//結果:Uncaught TypeError: Cannot read property 'length' of undefined
上記の例ではnameは宣言されただけで、何も値が代入されていません。
そのため変数nameの中身はundefinedになっているのですが、
ifの条件句の中では
name.lengthという形で、
nameのプロパティを呼び出しています。


当然undefinedにはlengthなんて名前のプロパティはありません。
そのため、エラーが生じてしまうのです。


それを防ぐために、
name &&
という記載が追加されているのです。


つまり、nameに値が代入されていない場合は、
エラーが発生するプログラムに到達する前に、処理を打ち切りたいのです。

nameの中身がnullundefinedだった場合には
AND条件の左側の値がfalseであるとみなされますから、
その時点で処理が打ち切られ、
エラーが発生するname.lengthという処理が実行されなくなるのです。


この書き方はとても頻繁に用いられます。


また、場合によっては、以下のような書き方をすることも可能です。
if(!name || name.length <= 20){
  //正常時の処理
}else{
  errMsg = “名前は20文字以内で入力してちょ”;
}

今度は、
「name &&」
の代わりに
「!name ||」
という記述が付け足されています。

「!」はtrueとfalseを反転させるための演算子ですから、この記述でもnameが空だった際には真偽値の評価が打ち切られ、エラーの発生を防ぐことができます。



JavaScriptの論理演算では、最後に評価された値が戻される

JavaScriptの論理演算では、最後に評価された値が戻される

最後に、JavaScriptの論理演算のもう一つ特徴的な点として、
「最後に評価された値が戻される」
というものがあります。


たとえば、
console.log(true && “Hello”);

こんなプログラムを書いた時、
結果はどうなると思いますか?

文字列 “Hello” は、真偽値に直すとtrueとみなされるはずですから、
「true && true」で、
結果はtrueになりそうですね。


しかし、結果は、
「Hello」
です。


どうか混乱しないで下さい。

JavaScriptの論理演算では、結果が真偽値になるとは限らないのです。

前述のように、JavaScriptの論理演算は、結果が確定した時点で評価を打ち切りますから、
最後に評価された値をそのまま結果にしてしまえば、問題なく判定ができるはずです。

分かりにくいですね…


つまり、どういうことかというと…

if(true && “Hello”){
  // true時の処理
}

のようなプログラムを書いた時、

「true &&」の時点ではまだ結果が確定しませんから、ここではまだ評価を打ち切らず、次の”Hello”を評価するのですが、ここでJavaScriptは、この最後に見た”Hello”をそのまま結果にしてしまうのです。

したがって、さっきのif文は、以下のようになりますね。

if(“Hello”){
  // true時の処理
}

ここで、if文の条件句の中に入っている値は、真偽値として評価されるので、
”Hello”を真偽値であるtrueに変換し、結果としてこのif文自体の判定は、trueとなります。

こんな感じで、
JavaScriptのANDやORの判定は

  • 結果が確定した時点で処理を打ち切る
  • 最後に評価した値をそのまま結果として返す

という仕組みを用いて、成り立っているのです。


この仕組みを使ったのが、この記事の冒頭で見た、2つ目の例です。
const message = errMsg || “正常っす”;

この処理では何がしたいのかというと、
errMsgの中身が空っぽかどうかによって、messageに代入する値を変えたいのです。


処理としては、まずerrMsgの中身を確認して、
  • errMsgに何かが入っていれば、それをそのままmessageに代入
  • errMsgに何も入っていなければ、 ”正常っす” をmessageに代入

ということを行っています。

文字列は、空文字列("")でなければtrueとみなされますから、
errMsgに何か空でない文字列が入っていれば、その時点でOR演算の結果がtrueに確定するため、
その時点で評価を打ち切り、最後に評価したerrMsgの値が
そのままmessageに格納されるのです。

逆にerrMsgに空文字列が入っていたり、undefinedだったりすると、
その時点ではOR演算はまだ打ち切られないため、次の文字列 "正常っす" が評価され、
その値がそのままこのOR演算の結果となるのです。



このように、
「変数に値が入っていればそれをそのまま使うけど、空っぽだったら代わりのものを代入する」
というような処理を書きたいとき、
OR演算子がよく使われます。


慣れるととても便利な書き方ですが、知らずに遭遇してしまうと、
「え?なんで文字列がORで繋がってるの!?」
と、混乱してしまうかましれませんね。


特殊な書き方もぼちぼち覚えて、現場で困らないようにしましょう。


JavaScriptでは「A or B」と「B or A」の結果が異なることも…

JavaScriptでは「A or B」と「B or A」の結果が異なることも

一般的に、
「A or B」「B or A」
の結果は同じになるはずですよね?

しかしJavaScriptでは、これらの結果が異なる場合があることに、お気づきでしょうか…


たとえば、以下のような場合…
const a = true;
const b = “Hello”;

console.log(a || b);
// 結果:true

console.log(b || a);
// 結果:Hello


これは、JavaScriptの論理演算が、最後に評価した値を返すことによって起こることです。

だからどう、ということはありませんが、面白い性質です。

やっぱりJavaScriptの論理演算は、ちょっと変わってます。


お決まりの書き方は、仕組みを理解して覚えてしまおう

お決まりの書き方は、仕組み理解して覚えてしまおう

この記事の例で見たようなJavaScriptの書き方は結構「お決まり」の書き方だったりするので、仕事で遭遇することも多いと思います。

良く出会うからこそ、仕組みを理解しておかないと、足元をすくわれてしまうかもしれません…

特に、空文字列("")や0が、undefinedと同じ扱いをされることは覚えておかないと、
入力チェックなどで、思わぬ考慮漏れを生むこともありそうです。


もし、こういった書き方を見たことがなかった方や、仕組みが分からず何となく書いていた方がいらっしゃれば、
この記事がお役に立ちましたら幸いです。

最後まで読んでくださり、ありがとうございました!