デフォルト引数はundefinedの時しか使われまへんで(JS)
先日プラハチャレンジのメンターセッションの一環でコードレビューしていたらデフォルト引数を使った以下のような関数を
function hoge(input: string = "default") {
console.log(input)
}
こんな風に呼び出して
hoge("")
デフォルト引数が使われること(出力が"default"になる)を期待しているコードがあったので「デフォルト引数は引数がundefinedの時しか使われまへんで」って話をしました
デフォルトパラメータが使われるのはundefinedの時だけ
false
とか""
とか0
とか[]
みたいな値は普段から条件式に使われることも多いのでデフォルト引数の対象になりそうなイメージを持ってしまいますが、こいつらは例えば 「0という値を持っている」 とか 「空の文字列を持っている」 状態なので、「引数が指定されていない時(≒引数の値が分からない時)」 に適用されるデフォルト引数は代入されません
なので以下のような呼び出し方ではデフォルト引数は使われない:
hoge(false)
hoge("")
hoge(0)
hoge(null) // null(値がない事が分かっている)とundefined(値がわからない)は区別されるので
以下のような呼び出し方ではデフォルト引数が使われる:
hoge()
hoge(undefined)
気をつけてちょ
おまけ
「言語仕様には気をつけような!」ってことを伝える上でふと思い出したのですが if(hoge)
的なコードを見かける事が結構多いので、ちょっとif文の挙動周りに関しても思ったことをば。
if ("0") {}
これはtrueになるでしょうかfalseになるでしょうか?
...
正解はtrueです。では続きまして
if (0) {}
これはtrueになるでしょうかfalseになるでしょうか?
...
正解はfalseです。"0"の時はtrue、0の時はfalseになるわけですね。ではこれはどうでしょう?
0 == "0" // TSではなくJSだと仮定してね
...
正解はtrueです!
初めてみた時はちょっと面食らうかもしれませんが、JSでは==
オペレータを用いて比較する際にはこちらのアルゴリズムが用いられるので今回のケースでは"0"
をToNumberで0
に変換してから比較を行います。
あれっ、でも先ほどif("0")
はtrueでしたよね。こちらではToNumberによる変換は行なっていないのでしょうか?これはECMAScriptのif文の仕様を覗いてみると分かるのですが、内部的にToBooleanを用いて「if文に渡された引数がStringだった場合、文字列のlengthが0ならfalse、それ以外はtrueを返す」という、別のロジックが適用されているみたいですね。
ちなみに
if (new Boolean(false)) {console.log('hoge')}
これはtruthyです。hogeと出力されます。直感には反するかもしれませんがToBooleanの判定表を覗いてみると...
こちらの表によるとObjectの場合はBooleanだろうがPersonだろうがArrayだろうがその値を問わずtrueを返すようになっていますね。typeof new Boolean()
は'object'
なので、その値がfalseであろうともtrueになるわけです。
元の話に戻ると、結構カジュアルにif(hoge)
みたいな書き方を(かつhogeはboolean型ではない時に)見かけるのですが、せっかくならif (hoge === ...)
と厳密等価演算子(===)を使って丁寧に条件式を書いたほうが想定外の挙動が減って嬉しいのではなかろうか?と思ったり。慣れていないうちは(特にJSは?)等価判定で面食らうこともあると思うので、ちょっと自信がなければこちらのサイトを覗いてみると良いかもしれません。
if文にせよデフォルト引数にせよ、いつも当たり前のように使っている言語機能の中には意外と挙動を見落としているものがあるかもしれないので、時々自分でも挙動を試してみると良いと思うでよ〜と感じた次第です
Discussion
暗黙的な型変換に関してESLintにこのようなルールがあります。