2
0

More than 3 years have passed since last update.

やらかしJS先生がみた関数パラメータの闇({name, value = 0}) => console.log({name, value})

Last updated at Posted at 2020-01-07

Overview

過去にバグになってしまったものを忘れないよう書き留めておくシリーズです。
今回の題材は関数パラメータのデフォルト引数です。


const print = ({name, value = 1}) => console.log({name, value});

print({name: "value is null", value: null});
print({name: "value is undefined"});

Target reader

  • この結果がわからない方

Prerequisite

  • JavaScriptを一通り理解している

Body

答え合わせ

正解はこうなる。

> Object { name: "value is null", value: null }
> Object { name: "value is undefined", value: 1 }

どうだろう?value: nullにnullが来ることを予想できただろうか?
私はできなかった:joy:
valueにnullは存在しない前提だったので、nullレコード大量発生に泣いた:cry:

ちなみにオブジェクトじゃなくて、分割したらどうだろう?


const print2 = (name, value = 1) => console.log({name, value});

print2("value is null", null);
print2("value is undefined");

答えはこうだ。

> Object { name: "value is null", value: null }
> Object { name: "value is undefined", value: 1 }

オブジェクトかどうかは関係なく、単純にデフォルト値はundefinedに作用してnullには作用しないということだ。
MDNにこの定義はあるのか見たら、undefinedに作用するとはあった。

関数のデフォルト引数 は、関数に値が渡されない場合や undefined が渡される場合に、デフォルト値で初期化される形式上の引数を指定できます。

違和感を解消する

MDN見る限り、デフォルト値はundefinedにしか作用しない。理解した。
だけど、よく考えると今まで下記のように使い慣れたもののはず。


const print = (v) => console.log(v);

print(null|| undefined || true); // true
print(!null); // true
print(!undefined); // true

この原因を究明するためMDNを漁った。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Logical_Operators

true に変換できる値は、いわゆる truthy です。false に変換できる値は、いわゆる falsy です。

false と見ることができる式の例は、null、0、空文字列 ("")、あるいは、undefined と評価されるものです。

…まとめると、論理演算子は非常に感覚に沿ったものだが、デフォルトパラメータはそれとは別物ということだ:wink:

Conclusion

言語の理解が浅いとこんなことをやってしまう例として紹介しました。
本来であればテストコードで防御したかったのですが、開発優先だとなかなかテストコードを追加する時間を作れず…:persevere:

デフォルトパラメータが仕事するのはundefinedの時だけ(パラメータなしを含む)

Appendices

undefinedの使いどころ

nullとundefinedの使い分けは明確なものはないようですね。
TypeScriptではnullは使わないといっていますが、これはContributorのため物で一般的な規約ではないといっています。

Use undefined. Do not use null.

他の言語を習っているとnullを使うかと思いますが、undefinedを使うと効果的なユースケースが一つあるので紹介しておきます。
そのユースケースとはJSONにシリアライズするときです。

変換の際に undefined、 関数 (Function)、シンボル (Symbol) は有効な JSON 値ではありません。変換中にそのような値に遭遇した場合は、 (オブジェクトの中で発見された場合は) 省略されたり、 (配列の中で見つかった場合は) null に変換されたりします。

WebAPIでリクエストを返す際、値が無効なフィールド名を除外したい場合があります。
その無効な値にundefinedを設定することで、条件分岐や三項演算子等なしに除外することが可能です。
上記の引用にある通り、配列でのundefinedは除外されませんので、その場合はfilter(v => v)等で除去しましょう。
(filter(v => v)はfalsyとして定義された値 (false, 0, "", null, undefined, NaN)が該当するため、除外してはいけないものがないことに注意すること)

References

分割代入
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment

デフォルト引数
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions/Default_parameters

undefined
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/undefined

null
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/null

等価性の比較と同一性
https://developer.mozilla.org/ja/docs/Web/JavaScript/Equality_comparisons_and_sameness

論理演算子
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Logical_Operators

JSON.stringify()
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0