JavaScript

JavaScriptでうっかり躓く小石

More than 1 year has passed since last update.

JavaScriptでこれまでにちょっと引っかかったけど、あとから考えてみればものすごくヘボなことを並べます。
探せば無限に出てきそうだけど、とりあえず3つ!

条件演算子

駄目な例
var a = "1" + true ? "10" : "";
console.log(a); // "10"
動く例
var a = "1" + (true ? "10" : "");
console.log(a); // "110"

why?

初っ端からJavaScriptと厳密には無関係ですが、やらかした時にひどく困惑した例です。
原因は単純。条件演算子の評価順は演算子の中でも後ろの部類です。
(いや、こんなんやらかすのは私ぐらいかもしれませんが……何年コード書いてんだか)

基本的に論理演算が絡むと大抵優先順位は低くなるもんですが、条件演算子は更に輪をかけて低くなるものです。
それを忘れてうっかり上記のようなコードを書くと「あれ?」となります。

RegExp と Function Scope

駄目な例
var a = /あ/.test
動く例
var a = str => /あ/.test(str)

why?

今度はちょっとJSっぽい話です (コードは……まあバカですが……) 。
「駄目な例」は「JSって関数が第一級オブジェクトなんだろ? /あ/.testを変数に握らせたらそれで呼び出せるんじゃね??」とか考えて実験したコードです。無論動きません。
何故そんなの実験したかって?疲れてたんでしょう。

ではこれが何故動かないのか?
/あ/.testが指し示す関数は実のところRegExp.prototype.testです。
これがJavaScriptの面白いところで、prototypeでキッチリ構築された (つまり標準APIやしっかりしたライブラリの) オブジェクトのメソッドは、その所属オブジェクトをthisとした上でprototypeの関数を呼んでいるだけなのですよね。
今回の場合で言うと、変数aにぶち込まれたのは/あ/.test===RegExp.prototype.testです。
これは本来thisがRegExpオブジェクトであることを前提としている (所属オブジェクトの情報を基に比較する、当然ですね) ので、当然prototypeの関数だけ引っ張ってきてぶち込んだのではなんの意味もありません。

ちょっと脱線しますが、「じゃあprototypeの関数って単独だとなんの価値もないの?」というと、実はそうでもなかったりします。
Function.prototype.callやFunction.prototype.applyを使うと、任意の値をthisとして引き渡して動作させることが可能です。
有名な使いみちはquerySelectorAllで拾ったNodeListに対し、Array.prototype.forEachを適用するパターンでしょうか。

isActive? isInactive?

よろしくないかもしれない例
function hoge(isInactive){
  if(isInactive) return;
  // do something
}
ちょっといいかもしれない例
function hoge(isActive){
  if(isActive) {
    // do something
  }
}

why?

何がイケないんだネストは浅いほうがいいだろ!
そう思ったあなたは正しい。私もそう思います。
しかし重要なのは、「言語と状況によって、どちらがより安全側に倒れるかは異なる」という、ごく単純で致命的な一点です。

上記の2つはどちらも引数を一つ取り、これを条件にifを使用してなにがしかの操作の実行/回避を制御しています。
前者と後者の差はどこで発生するか。言うまでもないですが、JavaScriptの型強制機能……以下のような動作です。

Boolean(null); // === false
Boolean(undefined); // === false
Boolean(NaN); // === false
Boolean("false"); // === true
Boolean({}) // === true

もしもある操作do sometingがNaNやUndefinedが紛れ込んだ時に動いてほしくない操作だった場合、前者を使ってはいけません。たとえ無駄ネスト (っぽいもの) を見ると蕁麻疹が出るような、私のような人種でもです。
もちろん、動かしたほうが安全であるならば前者を使ったほうがよいでしょう。ただ、普通に書けば、大抵は「ここはこの条件の時動かしたくないんだよなあ」という要件のほうがよっぽど多いはずです。

なお、ネスト嫌いの民へのステロイド剤もちゃあんとあります。
なにせJavaScriptはなんでもできるので、Booleanへ機能を足すなんてお茶の子さいさいです。

ステロイド剤(劇薬です!)
Boolean.prototype.ifTrue = function(func){
  if(this.valueOf()) { // this = Boolean
    return func();
  }
}
// usage: isActive.ifTrue(action)

うーん、まんだむ。改良の余地は山ほどある気がしますが、とりあえずある程度は使えそうです。
なにしろこれはBooleanのprototypeなので、やってきたオブジェクト (usageで言うところのisActive) がBooleanでなければ、そもそもundefinedで蹴られるはずなのです!わーお。
実際にここまでやるべきかどうかはともかく (遺憾ながらたぶんやるべきではないでしょう……) 、最終手段として考えてみるのはありかもしれません。