JavaScriptに関するお知らせ

SINCE2019
>
【JS】なぜ.find()は空振った時の戻り値がnullではなくundefinedなのか

【JS】なぜ.find()は空振った時の戻り値がnullではなくundefinedなのか


こんにちは!配列走査系のメソッドが大好きすぎるMizutani(@sirycity)です。1位は.reduce()、2位は.map()、3位は.filter()です。

今回はそんな走査系メソッドの一つ、.find()の不思議についてです。

言いたいこと

該当するものがないことを明示するのがnull。

該当するものがないことを警告するのがundefined。

結論

なぜ.find()は空振った時の戻り値がnullではなくundefinedなのか。色々調べましたが正確な所は分かりませんでした。ごめん。

でも、ひょっとしてこうなんじゃない?と思える理由はいくつかあった。

  • 「find」という英単語には「あるかないか分からないものを見つける」ではなく、「確実にあるものを見つける」といった意味があり、「見つからないことが異常系として認識されている」ためundefinedが返る
  • 歴史的経緯によりECMAScriptはnullとundefinedを正確に表現できていない

このどっちか(もしくは両方)じゃない?といった結論に至りました。

findについて

.find()は配列に対して使えるメソッドで、条件を満たす最初の値をreturnします。これ見れば↓分かりやすい。

const prefs = ['北海道', '青森', '秋田', '岩手', ...以下略]

prefs.find(p => p.includes('島'))
// 福島

これは全ての都道府県から「島」って漢字が入る最初の都道府県を求める使用例です。福島が返ってきました。簡単! .find()は使い方自体はすごい簡単です。

findは0件の時nullじゃなくてundefinedが返る

ここからが本題です。次に全ての都道府県から「滅」って漢字が入る最初の都道府県を求めます。もちろんそんな都道府県はないので戻り値はnullのはず...やってみよう。

prefs.find(p => p.includes('滅'))
// undefined

なん...だと...

確かに.find()は空振りましたが、戻り値はundefinednullではありませんでした。

なぜundefinedだとおかしいのか

undefinedとnullの違い

undefinedは日本語にすると「定義されてない」です。もうちょい意訳すると、「お前はその値あると思ってるみたいだけど、実際にはそんな値ねえからundefinedにしとくわ」って感じです。まあ意訳はいいとして、重要なのは異常系であるということです。分かりやすく言うと、「間違いのない完璧なプログラムにはundefinedは出てこない」わけです。

一方nullは...nullの説明むずいんですが、一言で言うと「ないことが分かる」って感じ。「お前の言った条件で調べたけど一致するデータ一つもなかったわ」って感じです。重要なのは正常系である点です。厳密に言うとnullにはいくつか種類があってwikipediaとかに詳しく載ってるんだけど今回は割愛するね。

undefinedとnullと0の例

ネバーランドにある全ての県のうち「son」が含まれる最初の県を求める とかだと答えはundefined。ネバーランドなんて国ない(=定義されてない)からね!

アメリカにある全ての州のうち「son」が含まれる最初の州を求める とかだと答えはnull。実際そんな州ないんだけど「ひょっとしたらあるかも?」とは思うじゃん。でも結果的にない事がわかった。これが「ないことが分かる」って感じね。

ちなみに紛らわしい概念として0があるのでついでに。例えばサッカー日本代表の全試合のうち得点が3以下だった最初の試合の得点数とかだと戻り値は0になります。0は特別な数字じゃなくて結果の一つ!って考えると分かりやすいかも。

nullとundefinedについては昔の記事でもちょっと説明してるよ。

おかしい点

都道府県の滅の例に戻りましょう。

prefs.find(p => p.includes('滅'))

この戻り値はnullが正しいと考えます。なぜなら、このプログラムを実行したことによって「滅を含む都道府県がないことがわかった」からです。

しかし戻り値がundefinedです。これを解釈すると以下のような誤解を招くのではないでしょうか。

  • ひょっとして日本に都道府県ってないの?
  • 「日本の都道府県」って検索できないの?1つしかないとか?...規制がかけられてるとか?
  • 「滅」って漢字じゃないの?旧字だったり?中国語の似た文字間違えて打ってたりしない?
  • 「都道府県名」って漢字じゃないの?絵文字が入った県なんてあったっけ?

いやこんな間違いするわけないじゃんと思ったあなたへ。じゃあこれ全部アメリカの例にしたらどうでしょう。

  • ひょっとしてアメリカに州ってないの?
  • 「アメリカの州」って検索できないの?1つしかないとか?...規制がかけられてるとか?
  • 「son」ってアルファベットじゃないの?ウムラウトだったり?ドイツ語の似た文字間違えて打ってたりしない?
  • 「州の名前」ってアルファベットじゃないの?ウムラウトが入った州なんてあったっけ?

これならちょっと勘違いしそうじゃない?

...もちろんこれらの推測は全て誤ったものですが、undefinedが異常系であるために検索結果が0件だったという正常系の事象が何らかの不具合で正常な処理ができなかったという異常系の事象である可能性を惹起させるものになってしまっています。

分かりやすく言うと、正しい処理なのに間違った処理だと勘違いさせてしまうんです。これが非常にまずいと個人的には思います。

なぜこうなったのか

findに「確実にあるものを見つける」という意味がある説

findの戻り値がundefinedであるという事実を無理やり正しいものとして解釈すると、「検索対象がない」という事象が異常系である、つまり検索する段階で検索条件に一致するものが少なくとも1つは存在することは分かっているという事象が起きていることになります。

ここからは仮説ですが、もし「find」という英単語の意味として、「あるかないか分からないものを見つける」 ではなく 「確実にあるものを見つける」 というようなニュアンスがあれば、検索結果が0件だった時に 「1つは確実にあるはずなのに1つもないから異常系」 といった解釈ができるかも...しれません。

このへんの細かいニュアンスはちょっと調べたけど分からなかった。もし詳しい人がいたら私のtwitterにこっそり教えてください(唐突な宣伝)

歴史的経緯により仕方なく

どっちかと言うとこれの気がする。ぶっちゃけ、言語仕様決めた時はnullとかundefinedとかあんま考えてなかった...みたいな。2000年代初頭のJavaScriptが担うそのあまりにちっぽけな役割から邪推すれば、そんくらい適当に決めちゃった可能性もあるかなーとは思います。

ファインド... アンディファインド... よし、undefinedだ!みたいな。軽すぎか。

例えば同じイテレーション系のメソッドで.indexOf()とかありますが、indexOfは空振ると-1が返ります。もうnullとかundefinedが可愛く見えるレベルで意味不明です。-1だと後ろから2番目かと思うし(pythonはそんな感じの挙動)、そもそも-1はfindとかでは普通に結果としてありえるのでますます混乱します。「石川遼のプロ入り初めてのスコア」とか。知らんけど。

余談

nullとundefinedは非常に難解な上にこんな感じで言語仕様としても適っ当なので、nullは使わずにundefinedに統一するなんて規約もあったりします。有名どころだとmicrosoftの開発チームとか。

まとめ

  • findは空振るとundefinedが返る
  • その理由は不明
  • findって英単語の持つ意味かな?
  • もしくは適当に決めただけかも

結局のところ、「findは空振るとundefinedが返る」って覚えとくしかないですね。僕は3ヶ月に1回は忘れます。以上。

追記: この記事について重要なヒントを頂けました。ありがとうございます!



PREV
2020-06-13
【JS】React系とVue系のフレームワーク色々比較まとめ

NEXT
2020-07-02
【JS】flatMapの使い方とreduceやfilterでの代用について