Pages - Menu

Pages - Menu

Pages

2024年6月24日月曜日

JavaScriptのundefinedは必要ですか?nullと区別する必要はありますか?

https://jp.quora.com/JavaScript%E3%81%AEundefined%E3%81%AF%E5%BF%85%E8%A6%81%E3%81%A7%E3%81%99%E3%81%8B-null%E3%81%A8%E5%8C%BA%E5%88%A5%E3%81%99%E3%82%8B%E5%BF%85%E8%A6%81%E3%81%AF%E3%81%82%E3%82%8A%E3%81%BE%E3%81%99%E3%81%8B

https://jp.quora.com/JavaScript%E3%81%AEundefined%E3%81%AF%E5%BF%85%E8%A6%81%E3%81%A7%E3%81%99%E3%81%8B-null%E3%81%A8%E5%8C%BA%E5%88%A5%E3%81%99%E3%82%8B%E5%BF%85%E8%A6%81%E3%81%AF%E3%81%82%E3%82%8A%E3%81%BE%E3%81%99%E3%81%8B


大島 芳樹
·
フォロー
元 Viewpoints Research InstituteのComputer Scientist (2007–2014)4年前
この質問の答えには歴史的経緯があり、別の言語機能が初期開発時に変更された際の相互作用という混乱があり、ネット上で見られる作者以外からの答えにも後付けされた正当化がありということでなかなか興味深いものです。

歴史的経緯でBrendan Eich本人が述べている理由(のようなもの)は、

JavaScript history: undefined

にある情報とそこから辿れるBrendanのTweetが一次情報でしょう。ご存知のように、JavaScriptは、開発途中でJavaに似た構文にするように、という指示があったためにJavaの(問題ある)言語仕様の方にも影響を受けたわけです。

上記のページで述べられている説明としては、「Javaの変数にはオブジェクト型とプリミティブ型がある。Javaのnullはオブジェクトが存在しないということを表すが、JavaScriptではプリミティブ型変数を初期化しないでおくことができ、その場合はオブジェクトでもないけど値が存在しない、ということを表す値が必要であり、かつもし数値に型変換した場合に0にならない値が必要だった(と思った)ので、undefinedという別の値を導入した」ということです。ゆるい言語仕様のために、普通はどちらを使ってもなんとなく同じ結果になることが多いJavaScriptですが、以下のケースでは「明らかに違う種類のものだ」という思想が感じられます。

Number(null) => 0
Number(undefined) => NaN
が、質問に戻って「必要なのか」と言われると、普通のJavaScriptのレベルで考えている限りは必要はないだろうというのが私の結論ではあります。言語の家系図としてJavaScriptのおばあさんにあたるSmalltalkを見れば、「変数には型はなく、プリミティブ型という鬼っ子もないのだから、null一種類あればよいはず」ということで終了です。存在しないフィールドをアクセスした時にnullでなくundefinedが得られるのも「なぜ勝手にプリミティブ型だと仮定しているの?」という疑問がわきますし。初期化されない変数というものを考える必要もなく、もし初期化子がなければnullで初期化されていると思えば良いわけですし。Brendanは例外処理がなかったから、とか言っていますが、イベントドリブン式で実行されるJavaScriptなので、あるイベントハンドラでエラーがあったからと言って、そのエラーの後を無理やり実行しなくてはならないという真の理由はなかったはずです。

念のため書いておきますが、たかがif文の条件節でなるべくエラーが出ないようにするというために、falsyな値をfalseとして扱うというのは本当に大きな問題だと思います。

ただ、私はJavaScript上でFunctional Reactive Programming (FRP)言語を作ったことがあるのですが、その時には「確かに、オブジェクトがない、という値と、値が定義されていないという状態を別のものとして扱うことでシステムが美しくなる」ということに気がつきました。FRPで扱う「時間」には連続時間と離散時間がありますが、離散時間上の変数には、確かに値が存在しないということがあり得るわけです。実装の方でも、制約解消方式で実装した時に、あるノードの計算をするかどうかは依存する値がnullかundefinedかで変わってくることになります。

閲覧数:2,014回
32件の高評価を見る
1件のシェアを表示
広告

岡部 健さんのプロフィール写真
岡部 健
·
フォロー
情報技術者・開発者・Geek4年前
undefinedとnullの両方必要なの?

とあるプログラミング言語 (http://d.hatena.ne.jp/keyword/%A5%D7%A5%ED%A5%B0%A5%E9%A5%DF%A5%F3%A5%B0%B8%C0%B8%EC)が集う女子会にて:

Perl: そういえばさ、なんでJavaScriptちゃんってundefinedとnullの両方もってるの?
JavaScript: えっ、未定義の変数にアクセスした時undefined返したいじゃない?
Python: 例外投げて死ねばいいじゃん
Ruby: 例外投げて死ねばいいよね
Python & Ruby: ねー♡
Java: いやそこは参照型ならnull、数値型なら0で初期化すべきでしょ
C: これだから最近の若い子は…初期化にだってコストが掛かるんだからね!デフォルトで初期化するなんて無駄遣いよ!必要な人だけが責任をもって初期化すべきなのよ!
Perl: (Cおばさん怖い… Javaさんもなんか型苦しいし…) でもね、仮に初期化するとして、nullかundefかどっちか片方あればいいでしょ?
Lisp: (あらあら それを言うなら nil と 空リスト も 片方でいいでしょ (と 思ったが 言葉には しない) そっと #t を 飲む)
JavaScript: うーん、そうかなぁ。今彼氏がいないのと、生まれてからずっと彼氏がいないのは別じゃない?

空リストからpopしたら?

JavaScript: 例えばさ、空のリストからpopした時ってundefined返したいじゃない?
Python: 例外投げて死ねばいいよね、Rubyちゃん
Ruby: え?nilを返すでしょ常識的に考えて
Python: は?じゃあnilの入っていたリストからpopした場合とどうやって区別するの?
C: これだから最近の若い子は…私は成功した時に0 <= n < 256の値を返す関数でも、失敗した時に-1を返すためだけにint型にしているというのに!まったく考えなしのゆとりなんだから!
Ruby: (Cおばさん、めんどくさっ) Lispさんはどう思いますか?
Lisp: (あらあら そんな昔のこと 忘れたわ (と gosh を 起動して 試してみる)) (car ()) は 例外を投げる ようね*1

存在しないキーへのアクセス

JavaScript: あとオブジェクトに指定したキーがないときもundefinedを返したいの
Python: 例外投げて死ねばいいのに。私だったらobj.has_key(k)でチェックできるようにするなぁ *2 *3
JavaScript: 今はそんな感じのことも出来るんだけど、たしか昔はできなくて…
Python: さっさと例外投げて死ねばいいのにねー、ね、Rubyちゃん
Ruby: nilを返すけどなにか文句ある?*4
Memcached: (私はfalseを返すけどなぁ、と思っているが、言語じゃないので話の輪に入れない)*5
Python: ふーん、で元からnilが入っている場合にはどうするの?(ニヤニヤ)
Ruby: (-_-#)
Haskell: いまさらundefinedとかnullとかでフラストレーション溜めてるおばさんたちはモナド使った方がいいにゅ
JavaScript: Haskellがブラウザで動くようになったら使うかもね、Maybeね
Python: その可能性はNothingね*6
Haskell: …(-_-)

まとめ

Python: でもま、CさんやHaskellちゃんの言うとおり、返り値でエラーを伝える場合、それは成功した時に返されうる値以外でなきゃダメよねー、成功したのか失敗したのかわかんなくなっちゃうもんねー(とニヤニヤしながらRubyの顔を見る)
Haskell: (賛成してもらえてとても嬉しいのだが、その感情はIOモナドに押し込められて無表情)
Python: そういう意味で、リストや属性に入れられるnullと、失敗を伝えるための無効値undefinedを区別するのは合理的ね。JavaScriptちゃんもいいところあるのよね
JavaScript: あれ、いい忘れたけどundefinedはリストに入れられるよ
Python: はぁ?!死ねばいいのに!!

言語女子会: undefとnullは両方必要? - 西尾泰和のはてなダイアリー

JavaScriptのundefinedは必要ですか?nullと区別する必要はありますか?に対する大島 芳樹 (Yoshiki Ohshima)さんの回答

と概ね同意見ですが、特に以下の言及

ただ、私はJavaScript上でFunctional Reactive Programming (FRP)言語を作ったことがあるのですが、その時には「確かに、オブジェクトがない、という値と、値が定義されていないという状態を別のものとして扱うことでシステムが美しくなる」ということに気がつきました。FRPで扱う「時間」には連続時間と離散時間がありますが、離散時間上の変数には、確かに値が存在しないということがあり得るわけです。

については痛感しており強調したいです。

`null` に関しては使うべきではないと言われる一方で、自分はJavaScriptのundefinedをFRPライブラリの実装コードでもFRPライブラリの利用局面でも便利に使い倒しています。

30分でわかるJavaScriptプログラマのためのモナド入門

5.6. モナド関数を設計する 
自分自身を削除するモナド関数

また著者が最近書いたFRPオブジェクトをモナドになるように設計していると、値が undefined になればイベントを発生しない、というデザインに自然になってしまいました。人工的な仕様がオブジェクトの構造を要請するのではなくて、数学的な構造が自然と仕様を要請してしまう、というのは驚くべきことです。

timeline-monad is Timeline Monad

stken2050/timeline-monad

stken2050/timeline-monad

閲覧数:6,826回
65件の高評価を見る
4件のシェアを表示
広告

Junji Ueharaさんのプロフィール写真
Junji Uehara
·
フォロー
IT系企業に勤務更新日時:9ヶ月
JSにおいてundefinedとnullの意味は、歴史的経緯を無視すれば本来は明確です。

null…変数を参照で保持する単要素コンテナとしてみたとき、そのコンテナが「空」「何も指していない」ことを明示するための特別な値。プログラマが明示的に設定する。配列変数であれば[]に相当する値のスカラ変数バージョン。JSプログラムのある時点でnull値が発生していたとき、プログラマの意図によって行われたと推定して良い。伝搬可能。
undefined…変数を宣言している、あるいは取り出し操作が可能になっているが、まだ値が設定されていない場合に、取り出そうとした場合に得られる値。処理系が設定する。JSプログラムのある時点で、処理系が設定したundefined値が伝搬されて受け取っていたなら、それはおそらくどこかにバグがあったことの結果と見るべきで修正すべき箇所がある。
いずれも、値が取れない、という現象までは同じですが意味は違います。一般に「空であるという値を持っている」ことは変数値として正当であり正常系処理の範疇、値が設定されていない変数にアクセスしてしまうことはある種の異常系です。両者は排反な状況で曖昧さはありません。

問題なのは周辺事情です。

エラー含め値が取れなかったことを表す安易な関数返り値として、未定義でも空でもない意味でnullやundefinedを使用するケースがある。
その帰結として、空なのか未定義なのかというもとの意味を見失う。
そのようなライブラリが作られ、それを使った人がさらに真似をして広まり、またライブラリが作られ、という再生産
そのようなライブラリを使用せざるを得ない場合がある
安全サイドに倒し、どちらであっても動くように防御的にプログラムを書くためなおさら違いがわからなく。
その現実を踏まえて、どう扱っていけばよいかについてですが、

関数返り値としてエラーの意味でnullやundefinedを返す悪習を断つ。
{valid:boolean,value?:返り値 }のようにエラーを区別できるオブジェクトを返す
あるいは例外で返す。
非同期コールバックはpromisifyする。nullは.catch、非nullは.thenに流れるようにする。
関数の返り値の型を動的にしない(typescriptでいう、null許容、undefined許容、つまりnullやundefinedとのユニオンにしない)
変数はconstを多用し、let使うときも宣言と初期値指定を同時に行う
ここでもtypescriptで言えばundefined許容型、null許容型をさける。
typescriptの場合引数チェックは静的型で守る。
オプショナル引数は明示したもののみ許す。
デフォルト値を設定することでundefined非許容にできる
オプションオブジェクトの柔軟性は型でグルーピング分けしてユニオンで提供するなどし、実行時に先送りをなるべくしない。
値がないことを明示するためにnullを代入したりするのではなく(初期化後の代入や更新は、破壊的操作なのでそれ自体好ましくない)、値をそもそも設定しないことで(結果的にundefined)空を表す。
ローカル変数初期値にnullを与え、forやforEachのループで何かを探してローカル変数に代入する、というやり方を避け、mapやsome,findで探す。
要するにTypeScriptを使えば、曖昧にしてたことを曖昧にできなくなります。「似たような二種類の値がある」という現象を見るのではなく、個々の変数が生存期間を通じてどんな値を持ちうるかを、静的型で最低限の集合として吟味して絞り込み、保証できるようになるからです。

結果として、nullやundefinedを逃げに使う必要がなくなるので、区別の意識すらなくなっていくでしょう。3という値と"abc"という値を区別することに、普通迷ったり苦労する意識はないと思いますが、それと同じです。nullやundefinedが渡ってくることがないことが静的型で保証されれば、迷う必要も区別する必要もありはしません

具体的には、truthyや!=nullで判定することも含めて、まず「"undefined"」がソース上にはあまり現れなくなり、また関数型プログラミングを援用すれば、nullを表記上も、値として使うことも激減します。

つい、TypeScriptでの対策話も織り込んでしまいましたが、そこでどう解決されるかを理解してからJavaScriptに戻るのは有効だと思います。なぜならJSでも解決策を検討しやすくなるでしょうし、目をつぶるにせよ、JSの特性、もしくは限界として納得して目をつぶれるようになると思うからです。

ちなみに、TypScriptチームの開発コーディングルールは、nullの使用禁止[1]です。つまり、そこで不要とされてるのundefinedではなくnullのほうです。しかしこれは別に良い考えとみなされてはおらず、古い考え方のようです。

脚注

[1] microsoft/TypeScript
閲覧数:2,659回
26件の高評価を見る
広告

大島 芳樹さんのプロフィール写真
大島 芳樹
·
フォロー
Computer Scientist and Software Engineer, Math Teacher1年前
回答時の元の質問:JavaScriptでnullとundefinedをどのように使い分けていますか?
使用策定に深く携わった(私の知り合いの)Allen Wirfs-BrockやBrendan Eichが書いたHistory of Programming Languagesの論文 では以下のような記述があります。


つまりは、初期化されていない変数はundefined、あるいは存在しないプロパティーにアクセスしたときはundefinedであるが、「オブジェクトがない」という状態を示すためにnullを使うことを「想定(intended)」していた、ということです。

ただ、"intended"とは言っても、仮にnullだけあってundefinedが存在しないと言う言語仕様だったとしても多分誰も困らなかったと思うので、使い分けは確かに質問の通り各人の裁量によるところがあるでしょう。

私は基本的に「初期化されていない」という狭い意味の時にのみundefinedを使い、あとはnullで、ということでやっています。ですので、変数やプロパティーに代入する右辺値としてundefinedを使うことはありません。プロパティーを消すときはdelete演算子を使いますし、ある種のスイッチでオブジェクトがあるかないか、と言うことを示すのであれば常にnullを使います。また、「整数または値がないことを示す変数」があれば、それはnullか整数かと言うことになります。Cのようなレベルの静的型言語に慣れていると、「整数かnull」という変数があるのは気持ち悪いかもしれませんが、他のいろいろな言語からみるとそれほど変なことでもないでしょう。

3年ほど前に書いた回答ではまだ上記の論文が出ていなかったために別の記述を引用していますが、まあ結論は同じでした。ただ、以下の回答の最後に書いたFunctional Reactive Programmingを実装に関する話は、我ながら良い着眼点だったなと思いました。(やってみたことがある人は昔から知っていたことだと思いますが)。

大島 芳樹さんのプロフィール写真
大島 芳樹
· 4年前
JavaScriptのundefinedは必要ですか?nullと区別する必要はありますか?
この質問の答えには歴史的経緯があり、別の言語機能が初期開発時に変更された際の相互作用という混乱があり、ネット上で見られる作者以外からの答えにも後付けされた正当化がありということでなかなか興味深いものです。 歴史的経緯でBrendan Eich本人が述べている理由(のようなもの)は、 JavaScript history: undefined にある情報とそこから辿れるBrendanのTweetが一次情報でしょう。ご存知のように、JavaScriptは、開発途中でJavaに似た構文にするように、という指示があったためにJavaの(問題ある)言語仕様の方にも影響を受けたわけです。 上記のページで述べられている説明としては、「Javaの変数にはオブジェクト型とプリミティブ型がある。Javaのnullはオブジェクトが存在しないということを表すが、JavaScriptではプリミティブ型変数を初期化しないでおくことができ、その場合はオブジェクトでもないけど値が存在しない、ということを表す値が必要であり、かつもし数値に型変換した場合に0にならない値が必要だった(と思った)ので、undefinedという別の値を導入した」ということです。ゆるい言語仕様のために、普通はどちらを使ってもなんとなく同じ結果になることが多いJavaScriptですが、以下のケースでは「明らかに違う種類のものだ」という思想が感じられます。 Number(null) => 0 Number(undefined) => NaN が、質問に戻って「必要なのか」と言われると、普通のJavaScriptのレベルで考えている限りは必要はないだろうというのが私の結論ではあります。言語の家系図としてJavaScriptのおばあさんにあたるSmalltalkを見れば、「変数には型はなく、プリミティブ型という鬼っ子もないのだから、null一種類あればよいはず」ということで終了です。存在しないフィールドをアクセスした時にnullでなくundefinedが得られるのも「なぜ勝手にプリミティブ型だと仮定しているの?」という疑問がわきますし。初期化されない変数というものを考える必要もなく、もし初期化子がなければnullで初期化されていると思えば良いわけですし。Brendanは例外処理がなかったから、とか言っていますが、イベントドリブン式で実行されるJavaScriptなので、あるイベントハンドラでエラーがあったからと言って、そのエラーの後を無理やり実行しなくてはならないという真の理由はなかったはずです。 念のため書いておきますが、たかがif文の条件節でなるべくエラーが出ないようにするというために、falsyな値をfalseとして扱うというのは本当に大きな問題だと思います。 ただ、私はJavaScript上でFunctional Reactive Programming (FRP)言語を作ったことがあるのですが、その時には「確かに、オブジェクトがない、という値と、値が定義されていないという状態を別のものとして扱うことでシステムが美しくなる」ということに気がつきました。FRPで扱う「時間」には連続時間と離散時間がありますが、離散時間上の変数には、確かに値が存在しないということがあり得るわけです。実装の方でも、制約解消方式で実装した時に、あるノードの計算をするかどうかは依存する値がnullかundefinedかで変わってくることになります。
ただ、私はJavaScript上でFunctional Reactive Programming (FRP)言語を作ったことがあるのですが、その時には「確かに、オブジェクトがない、という値と、値が定義されていないという状態を別のものとして扱うことでシステムが美しくなる」ということに気がつきました。FRPで扱う「時間」には連続時間と離散時間がありますが、離散時間上の変数には、確かに値が存在しないということがあり得るわけです。実装の方でも、制約解消方式で実装した時に、あるノードの計算をするかどうかは依存する値がnullかundefinedかで変わってくることになります。

つまりは、時間に依存する関数の定義域がR全体ではない場合があるので、数学的な意味で「値がない」つまりは推移的にそれに依存するすべての関数も値がない、と言うことになる、ということをundefinedによって素直に表現できるようになるわけです。nullのほうは、「ない、という値がある」という意味で、それらは厳密に区別して扱うとわかりやすい、ということになります。

閲覧数:1,457回
18件の高評価を見る
1/1件の回答
広告

大枝 克行さんのプロフィール写真
大枝 克行
·
フォロー
プログラマ1年前
関連
JavaScriptの勉強をしていてnullとundefinedの違いなんてどーでもいい!と思ってしまいます。プログラマーに向いてないのでしょうか?
向いているか向いてないかは置いといて(回答拒否

こういう時は、なんでJavaScriptの設計者はnullだけじゃなく、undefinedを用意したのかに想いをめぐらせると良いと思います。

nullは他の言語でも多く使われる値で、いわば「0」的扱いをされてきました。

例えば、電卓の計算結果を格納する変数resultがあったとします。

計算結果というのは、Cボタンとかでクリアしたら消えます。

つまり、resultは「数値 or 無し」を表象するような変数である必要がありますよね。

そういう時に「無し」を表現するために使われてきたのです。

電卓は初期起動時には計算結果が「無し」であるわけですから、

let result = null
として初期化され、resultがnullの場合はUIの計算結果の欄には何も表示されないように、我々はプログラムを書くわけです。

では、undefinedはどういう状態を表現するために用意されたのかというと、変数はあるけど何も値を指していないことを意味するために用意されています。

let result
こうしたときに、勝手にresult->nullというようなポインティングはされません。

これはどの言語も同じです。

しかし他の言語だと、このままresultを使おうとするとエラーになります。

uninitializedがどうとか怒られます。

でもJavaScriptの場合は、怒られはしないけどundefinedが格納されるので、その値次第で好きにハンドリングするようにユーザー(開発者)に委ねられているのです。

これはなぜかというと、JavaScriptにおけるポリモーフィズム(多態性)の実現が関係してきます。

勉強中の方にいきなり難しい単語を出して申し訳ないのですが、一旦忘れてもらって、次に示す実例を見てください。

例えば、JavaScriptではこんなコードを書けます。

// aにはpiyoプロパティが無い
const a = {
hoge: "hogehoge",
fuga: "fugafuga"
}

// aにはhogeプロパティが無い
const b = {
fuga: "fugafuga",
piyo: "piyopiyo"
}

function handle(x) {
console.log(x.fuga);

// hogeプロパティが存在したらコンソールに出す
if (x.hoge !== undefined) console.log(x.hoge)
}

handle(a)
// fugafuga
// hogehoge

handle(b)
// fugafuga
handleという1つの関数でaやbという複数の様態を処理できていますよね。(ポリモーフィズム、多態性)

多態性は他にもいろんな種類があるのですが、これはロー多相という種類です。

ポリモーフィズム - Wikipedia
この項目では、プログラミング言語の概念について説明しています。化学用語については「 多形 」を、生物学用語については「 多型 」をご覧ください。 この記事には 複数の問題があります 。 改善 や ノートページ での議論にご協力ください。 ポリモーフィズム ( 英 : polymorphism )とは、それぞれ異なる型に一元アクセスできる共通接点の提供 [1] 、またはそれぞれ異なる型の多重定義を一括表現できる共通記号の提供 [2] を目的にした、 型理論 または プログラミング言語理論 ( 英語版 ) の概念および実装である。この用語は、有機組織および生物の種は様々な形態と段階を持つという 生物学 の概念からの借用語である [3] 。 多態性 、 多相性 と邦訳されることが多い。 ポリモーフィズムは、通常以下の三種に分けられる。 アドホック多相 (ad hoc polymorphism) 恣意的な型の集合に一つの共通接点を提供する。 関数オーバーロード 、 Mix-in のいち実装、 型クラス など。 パラメトリック多相 (parametric polymorphism) 詳細化されていない型要素を内包する抽象的な型に記号表現を提供する。 ジェネリクス や 関数型言語 の型構築子など。 サブタイピング (subtyping) サブタイプ多相(subtype polymorphism)やインクルージョン多相(inclusion polymorphism)とも。上位型をその下位型の数々で代替できるようにする [4] 。 オブジェクト指向 の多態性はこれを指す。 この他に、 ロー多相 ( 英語版 ) (row polymorphism)とポリタイピズム(polytypism) [注釈 1] も挙げられることがある。対義語はモノモーフィズム(Monomorphism)である。 ポリモーフィックな 型システム の研究は1960年代から始められている。 クリストファー・ストレイチー の1967年論文 Fundamental Concepts in Programming Languages ( 英語版 ) で、アドホック多相とパラメトリック多相という概念が初めて提唱されている [6] 。アドホック多相は 「ALGOL68 」で実践され、パラメトリック多相は「 ML 」の型システムで実践された。 1985年に ピーター・ウェグナー ( 英語版 ) と ルカ・カーデリ ( 英語版 ) は「 Simula67 」の 継承 +動的ディスパッチを説明するためのインクルージョン多相という概念を提唱した [7] 。これとストレイチー提唱の二つを合わせて三本柱にした概念が、ポリモーフィズムと呼ばれるようになっている。 1989年に オブジェクト指向 の 動的型付け を説明するための ロー多相 ( 英語版 ) という概念が ミッチェル・ワンド ( 英語版 ) の著作で提唱されているが、知名度は低い。同年にパラメトリック多相をモチーフにした ジェネリックプログラミング が アレクサンダー・ステパノフ ( 英語版 ) らの著作で提唱され、これはポリタイピズムとも呼ばれた [注釈 2] 。 1980年代の「 Ada 」は ジェネリクス に型制約と称する 有界量化 ( 英語版 ) の概念を採用した。1990年代の「 Haskell 」はアドホック多相とジェネリクスを融合した 型クラス を考案している。2003年の「 Scala 」は サブタイピング + ジェネリクス に 共変性と反変性 を導入した。 ポリモーフィズムの種類 [ 編集 ] アドホック多相 [ 編集 ] 関数や演算子の多重定義のように、同じ名前で型の異なる引数に適用できて、その振る舞いは引数の型によって違うような関数の多相性のことを「アドホック多相」という。「ad hoc(その場しのぎの)」という用語は悪い意味で使われているのではなく、単にこの種の多相性が型システムの基本的な機能ではないという事実を指して使われている。次の C++ での例では、 Add 関数は呼び出し側からは様々な型に対して総称的に動作するかのように見えるが、コンパイラから見ればこれらは全く別個の2つの関数である。 動的型付け言語では、実行されるべき正しい関数が実行時まで決定できない可能性があるという点で、状況はより複雑になりうる。暗黙の型変換も型強制多相(coercion polymorphism)としてアドホック多相の一形態と定義される [7] [8] 。 パラメトリック多相 [ 編集 ] パラメトリック多相を使うと、値の型に関係なく「一様に」値を扱うことで、関数やデータ型を総称的に記述できるようになる [9] 。パラメトリック多相は言語の静的な型安全性を保ちながら表現力を向上させる手法のひとつである。 パラメトリック多相の概念は関数とデータ型の両方に適用される。異なる型の値に対して評価、適用可能な関数のことを「多相な関数」という。総称化された型とみなすことができるデータ型(例えば任意の型の要素を持てるリスト)は「多相なデータ型」という。 パラメトリック多相性は関数型プログラミングの分野では至るところに現れるため、しばしば単に「多相性」と言われることがある。次の Haskell の例ではパラメータ化されたリストと2つの パラメトリック 多相な関数を示す。 data List a = Nil | Cons a ( List a ) length :: List a -> Integer length Nil = 0 length ( Cons x xs ) = 1 + length xs map :: ( a -> b ) -> List a -> List b map f Nil = Nil map f ( Cons x xs ) = Cons ( f x ) ( map f xs ) パラメトリック多相は様々なオブジェクト指向言語でも利用できる。例えばC++やD言語の テンプレート 、JavaやC#の ジェネリクス などである。 class List < T > { class Node < T > { T elem ; Node < T > next ; } Node < T > head ; int GetLen
https://ja.wikipedia.org/wiki/%E3%83%9D%E3%83%AA%E3%83%A2%E3%83%BC%E3%83%95%E3%82%A3%E3%82%BA%E3%83%A0
handle関数の中でやっている「hogeプロパティがあるのだから、これは変数aだろう」というように構造で判断するようなやり方のことです。

このやり方のメリットは、handle関数が極めて汎用的になることと、余分な情報が不要な分サクッと実装できてメモリ消費も抑えられる点です。(これは昔の貧弱なクライアントデバイスにとっては、今よりもっと嬉しいことだったのです)

JavaScriptはundefinedというたった一つの値を導入しただけで、ロー多相の実現をエコで容易なものにしてしまったのです。

これがもしnullしかなく、存在しないプロパティにアクセスした時点でエラーになっていたら、以下のように宣言しなければならず、冗長な上に、変数aにとっては何を表現しているわけでもないpiyo:nullという余分で、勘違いを生みかねない情報を持たねばなりません。

// aにはpiyoプロパティが無い
const a = {
hoge: "hogehoge",
fuga: "fugafuga",
piyo: null
}

// aにはhogeプロパティが無い
const b = {
hoge: null,
fuga: "fugafuga",
piyo: "piyopiyo"
}
あるいは、以下のようにもできますが、冗長である上に、handle関数の汎用性が落ちてしまいます。

// aにはpiyoプロパティが無い
const a = {
type: "A",
hoge: "hogehoge",
fuga: "fugafuga",
}

// aにはhogeプロパティが無い
const b = {
type: "B",
fuga: "fugafuga",
piyo: "piyopiyo"
}

// aと同じデータを持つが、タイプが違う
const c = {
type: "C",
hoge: "hogehoge",
fuga: "fugafuga",
}

function handle(x) {
console.log(x.fuga);

if (x.type === "A") console.log(x.hoge)
}

handle(a)
// fugafuga
// hogehoge

handle(b)
// fugafuga

handle(c)
// fugafuga
// Cのhogeプロパティは出ない!
(このやり方は、サブタイプ多相といってJava以来のOOPではメジャーですが、このように冗長である上にメモリも余計に消費する。ただし、設計上それが望ましい時もあるので使いどころを分けることが肝心。ES6以降はclassを使うとスマートに実現できるがprototypeでも可。)

という具合に微妙な違いにも着目してみると、プログラミングはより有意なものになるのです。

(他にもundefinedの効用があれば教えてほしいです)

結局のところ、JavaScriptは道具です。

道具をうまく使うにはそれをよく理解しないといけないのです。

そして、それはプログラマーだけに限った話ではないので、プログラマーに向いているかどうかの議論はできません。

例えば、トンカチを初めて見た人は、なんで左右非対称なのか疑問に思うでしょう。

そして、頭の方が大きくて持ちやすそうだからと、柄の部分で釘を叩いても、本来の効果は出ませんよね。

でもそういう時、上手にトンカチを使っている人を見て「あーそうやって使うといいのか」と学ぶわけです。

そして、それは注意を向けていれば誰にでもできることだと思いますよ。

また、

「使いづらいのはあなたのせいじゃない、でも使いこなせないのはあなたのせい」

という名言もあります。(嘘です。今私が作りました。)

ちなみにプログラマーに向いている向いてないの話は以下の回答でしているので参考にしてください。

「あっ、こいつプログラマーに向いてないわ。」ってどんな時に思いましたか?
https://qr.ae/pvfnfI
閲覧数:1,887回
14件の高評価を見る
1/7件の回答
広告

Yukihiro Matsumotoさんのプロフィール写真
Yukihiro Matsumoto
·
フォロー
プログラミング言語「Ruby」の作者更新日時:3年前
関連
JavaScriptに型は必要だと思いますか?
JavaScriptは動的な型を持つ言語であり、すでに(個別のオブジェクトには実行時の)型があり、欠かすことはできないので、答えは「必要」になります。

ただ、この質問の真意は「静的な型」が必要かどうか問うものだと思われるので、その点について考えてみましょう。静的な型にはコンパイル時に型のチェックを網羅的に行うことができることが最大のメリットで、JavaScriptにそのようなメリットが加わることを望む人はそれなりにいることでしょう。それこそがまさに「JavaScriptに静的な型を加えた言語」であるTypeScriptが存在し、それなりのユーザーを集めている理由です。

一方、静的な型を導入し、それを徹底させる(徹底させないと型チェックの網羅度が下がり、嬉しさが下がる)ことは、動的な型の持つ柔軟性の一部をあきらめることに繋がります。TypeScriptはそのような柔軟性を維持するために型システムに色々と工夫をしていますが、限界はありますね。そのような柔軟性を好む人はJavaScriptに静的型は不要と感じるかもしれません。また、型宣言を付け加わることによって、プログラムの記述量が増えることを嫌う人もいそうです。

静的な型のもうひとつのメリットとして、型情報を利用してさらなる最適化を行うというものもありますが、JavaScriptの場合、実行時型情報によって最適化を行うV8のような高速処理系が既に存在するので、こちらのメリットから必要とされることはないでしょう。

閲覧数:8,873回
56件の高評価を見る
1/4件の回答
広告

山本 聡さんのプロフィール写真
山本 聡
·
フォロー
プログラマー更新日時:4月29日
関連
JavaScriptでnullとundefinedで挙動が違うのはなぜですか?
undefinedという奇妙な値があるので誰もが疑問に思うところですよね。

空(から)を示す値は次のようなものがあります。

オブジェクト(や配列や関数とか)に対するnull
文字に対する空文字
数値に対する NaN(これも相当変な値だと思います)
というそれぞれで空(から)を表す状態があったにも関わらず、追加してundefinedが必要なのかというと、、普通はいらないだろうと誰もが思うところです。

よくは知りませんが、もともとの言語機能として、未設定値を参照してもエラーにならないようにしようとかいう工夫だったのかもしれません。

で、あとから「これいらないんじゃないのか?」と大勢が思ったとしても、すでに実装上存在してしまっていて、過去コードはそれを前提に作られてしまっているので、それを捨てるわけもいかずなので残っている、という状況でしょう。

以前、Qiita記事でそれらの区別について書いたことあります。

== null よりも ===null と === undefined を使おう - Qiita
Re: === undefined よりも == null を使おう - Qiita=== undefined よりも == null を使おう - Qiitaこちらの記事の反対のスタイルでコー…
https://qiita.com/standard-software/items/08597efad6dff1413897
長年使っていてその使い分けがわかってきたので、私は自分的な方針として、次のようにしています。

値初期化の場合に空を表したいならnullを使って自分で代入する
値が初期化すらされてないことを表すのはundefined なので、自分で代入することはない
読み取りのときにnullが取得できる場合はどこかで空で初期化されていることがわかる。
読み取りのときにundefinedが取得できる場合は、初期化もなしに値を読み取っていることがわかる。(そしてこれはちょっとまずい状況かもしれないと判明する)
たいてい、これは問題がおきないです。

また、下記の記事で紹介されているようnullを使わず全てをundefined側に寄せる、というのも、賢いプログラムの組み方だと思います。十分に受け入れられる優れた手法です。

undefinedは消せないけど、nullは使わなければ現れないので、undefinedだけ使うというやり方。

undefinedとnullの違い | TypeScript入門『サバイバルTypeScript』
多くのプログラミング言語で「値がない」を表現する方法は、nullなど1通りです。しかし、JavaScriptでは「値がない」に相当する表現にnullとundefinedの2通りがあります。他の言語からJavaScriptに来た人が驚き、使い分けに悩む部分です。ここでは、nullとundefinedの仕様上の違い、実際のコーディングでどう使い分けるべきかについて説明します。
https://typescriptbook.jp/reference/values-types-variables/undefined-vs-null
もうひとつやり方があって、これはこれはまずいやり方で、微妙な違いなのですが重要な違いがありことなのですが、

nullとundefinedがどちらが入ってきても同一視するというものがあります。

そのようなコードが多くあると、nullであるべきなのにundefinedになったりとか、逆であったりするときに不具合の箇所が特定しにくい問題を引き起こします。

nullとundefinedを同一視してよいとか、同一視するべきだ、と言ってたり書いてたりする情報源は信用しないようにしています。

JavaScriptプログラミングの腕が足りない人が書いた記事だからです。(typescript deep driveとか…)

よい教科書とそうでない教科書、よい先人とそうではない先人の言葉、を見分けなければいけないのはなかなか大変で、こういうのを見抜くには結構経験が必要かもしれません。

そんな感じに思いました。

あんまり回答になっていませんですが、そんな感じで使い分けしています。

閲覧数:5,267回
18件の高評価を見る
1/2件の回答
広告

Junji Ueharaさんのプロフィール写真
Junji Uehara
·
フォロー
IT系企業に勤務更新日時:1年前
関連
Typescriptで「any」ではなく「unknown」を使用する理由はなぜでしょうか?
いずれも、「任意のデータ型」を保持する引数や変数、プロパティ等に指定するための型です。

任意のデータ型とはどういうことかというと、たとえば実行時に「stringもしくはnumber」を保持することがわかっている型は、「string | number」の様に直和型を指定しますが、候補となる型がこのように具体的なものとして列挙しきれず、もしくは列挙したくなく、あらゆる型を保持できるようにしておきたい、というときに使用します。

anyとunknownのデータ型の違いを説明する前に、型を持つ変数や引数、プロパティ等に対する操作の種別について説明します。

プログラム中で使われる変数等に対して行われる操作は、読み取りと書き込みの二種類があります。例えば、変数xに対して

foo(x)
のように値を取り出して関数の引数に指定する場合は、xの読み取りの操作です。

z = x
のように代入文の右辺や、式での使用も読み取りの操作です。

メソッド呼び出し

x.bar()
も barというメソッドの呼び出しでthisにxを代入して呼び出すのですから、読み取りの操作です。

これに対して、

x = 999
のようにxを代入式の左辺に指定するのはxへの書き込み操作です。他、変数の初期化や関数呼び出し時の関数の仮引数への実引数の代入ないし初期化も書き込み操作です。

さて、anyとunknownの話に戻ります。

書き込み操作に対して、anyとunknownは同じです。任意の型の値を代入でき、コンパイル時の型チェックエラーになりません。

let a: unknown;
let b: any;
// 書き込み操作を試す
a = 1; // OK
b = 1; //OK
a = "abc"; // OK
b = "def"; // OK
// 読み取り操作を試す
console.log(a.toUpperCase()); // 型エラー
console.log(b.toUpperCase()); // OK。型エラーにならない
// 暗黙の型推論によるany型の伝播を試す
let d = b.toUpperCase(); // OK。ふたたび型エラーにならない
d.toUppeeffeerCase(); // anyへの操作の結果はやはりany
しかし、上のように読み込み操作に対して違いが出てきて、

anyは、データ型を無視した任意の読み出し操作ができ(コンパイル時の型チェックエラーにならない)、その成否は実行時のJSに委ねられる。また、その操作の結果の型はやはりanyになる。
unknownは、いったん特定の方に強制変換したり型ガードしたりしないと、読み出し操作が全て型エラーになる。
anyは動的型を許し、さらにそれが型推論を通じて暗黙に伝播していきます。(ただし明示的な型指定をした変数や関数引数には渡せないので一定の歯止めはあります。)

unknownは、任意の型の値をぶっこんだり、あるいは外部の何かが動的型であり、動的型のままハンドリングすることを許しますが、暗黙の型推論を通じてのunknown型の伝播が阻止されます。それが意識せずにtypescriptの型安全の世界に入ってくることを許しません。

C,C++をご存知であれば、anyはCのvoid*、unknownはC++のvoid*と言えば話が早いかもしれません。

使いわけですが、anyの使用を「自コード内では基本撲滅すべきもの」と位置付けた上で、anyの限定されたどうしても必要な用途のためにunknownが作られたと考えられます。

unknownの具体的な利用用途はそれほど多くはないように思います。なぜなら、外部の既存のJSコードの型は、実際には特定の型で綿密な型定義で明確化できるはずだからで、それ以外の「任意の型が格納できることが本質的に必要な変数やデータ構造」の型がunknownになるはず。

例を挙げれば、未知で真に動的なJSONを読み込むJSON.parse()の返り値や、sessionのような、任意の変数を保持するプールのようなデータとか、型無しのデータストアのようなものとか。しかしそれでもobjectで表現できる場合が多いでしょう。unknownはobjectに加えてプリミティブ型が保持できるものです。

あとは余談で私見ですが、unknownっていう名前、あんまりよろしくないような。anyとunknownは逆では。少なくとも、unknownは「dynamic」の方が良かったような…

こちらで紹介されました
コードのアイコン
コード ·
フォロー
Junji Uehara

さんの投稿4年前
"anyを「基本撲滅すべきもの」と位置付けた上で、anyの限定されたどうしても必要な用途のためにunknownが作られた"

閲覧数:5,307回
22件の高評価を見る
1件のシェアを表示
1/2件の回答
広告

Mukou Youichirouさんのプロフィール写真
Mukou Youichirou
·
フォロー
フリーランスのネットワーク技術者、ITサポート技術者、情報処理安全確保支援士(の試験合格)、Webサイト開発等 (2008–現在)更新日時:2年前
関連
null=開始前、0=開始、1=終了 nullに意味を持たせるべきではない理由を教えていただけませんか?
バグさえ起こさないならいいのかもしれませんけど、非常に筋が悪いと思います。

ご存じでしょうが、nullはnullという値ではなく「何の値でもない」という謎の値ですから、何の値でもないという値に意味を持たせようとすると無駄に手間がかかったり、エラーや意図しない動作の原因になったりします。

たとえば、リレーショナルDBで、


というテーブルがあるとして、これらをJOINして以下の「クエリ1」のようにしようとしてもnullの行は出てきませんよね。「何の値でもない」という値では結合のしようがないわけです。

同じく「クエリ2」のようにJOINした上で件数を数える表を作ろうとしてもINNER JOINでは出てきませんよね。(OUTER JOINをすれば一応は出ますが意味合いが異なってきます)


素直に「開始前」は0とするだけで済むことなのに、nullにしてしまうと「このテーブルにおいてnullには意味がある」という注意がシステム全体で必要になってしまうわけで、とても不合理です。

閲覧数:8,226回
58件の高評価を見る
1/8件の回答
広告

大島 芳樹さんのプロフィール写真
大島 芳樹
·
フォロー
元 Viewpoints Research InstituteのComputer Scientist (2007–2014)4年前
関連
JavaScriptのnullとundefinedはなぜわざわざ別になっているのでしょうか?
歴史的経緯(らしきもの)は、

JavaScriptのundefinedは必要ですか?nullと区別する必要はありますか?に対する大島 芳樹 (Yoshiki Ohshima)さんの回答

や、その質問に対するいくつかの回答の中で述べられています。抜粋すると

ご存知のように、JavaScriptは、開発途中でJavaに似た構文にするように、という指示があったためにJavaの(問題ある)言語仕様の方にも影響を受けたわけです。

上記のページで述べられている説明としては、「Javaの変数にはオブジェクト型とプリミティブ型がある。Javaのnullはオブジェクトが存在しないということを表すが、JavaScriptではプリミティブ型変数を初期化しないでおくことができ、その場合はオブジェクトでもないけど値が存在しない、ということを表す値が必要であり、かつもし数値に型変換した場合に0にならない値が必要だった(と思った)ので、undefinedという別の値を導入した」ということです。ゆるい言語仕様のために、普通はどちらを使ってもなんとなく同じ結果になることが多いJavaScriptですが、以下のケースでは「明らかに違う種類のものだ」という思想が感じられます。

Number(null) => 0
Number(undefined) => NaN
ですが、そちらにも書いたようにJavaScriptにはこの区別は必要なかったのではないかとは思っています。

閲覧数:1,928回
5件の高評価を見る
1/1件の回答
広告

Ohkubo Koheiさんのプロフィール写真
Ohkubo Kohei
·
フォロー
株式会社ハートレイルズのソフトウェアエンジニア (2010–現在)
·
1年前
関連
JavaScriptの勉強をしていてnullとundefinedの違いなんてどーでもいい!と思ってしまいます。プログラマーに向いてないのでしょうか?
どうでもよい問題をどうでも良いと切り捨てるのも1つの才能です。実際、多くのメジャ言語で null と undefined は区別しないので、おそらくどうでも良いのです。

だけど、どうでもよいで済まない場面がごくたまに[1]あります。

const obj = {key: undefined, array: [1, undefined, 3, undefined]};
console.log(JSON.stringify(obj));
// {"array":[1,null,3,null]}
挙動の異なる場面があるはずだ、それは何か?という事に思い至るのはソフトウェアエンジニアには必要な能力と思います。
脚注

[1] JavaScript の undefined と null を完全に理解する
藤沢 坂井 瞭介さんのプロフィール写真
藤沢 坂井 瞭介
·
フォロー
零株式会社の代表 (2022–現在)2年前
関連
NULLとは概念ですか?

これを見ると分かりやすいでしょうw 他との比較という意味でw

NULLと0(ゼロ) の違いww
off.tokyoでは、毎日ニュースか、コラムか、ネタを投稿してるのだが、今日はネタの日ですw …
https://off.tokyo/blog/null-0-chigai/
閲覧数:2,008回
15件の高評価を見る
1/4件の回答
広告

Chiyuki Edoさんのプロフィール写真
Chiyuki Edo
·
フォロー
2年前
関連
0 null undefinedの違いを数学で表現するとどうなってますか?
順に 0、null、undefined

{
0
}

{
}

{
x
0
}
Sugawara Katsunoriさんのプロフィール写真
Sugawara Katsunori
·
フォロー
システムエンジニア時々傭兵プログラマ
·
3年前
関連
スクリプト言語のプログラマーにとってnull安全という概念は無縁なのでしょうか?
暴論を言いますが、動的片付け言語は

「null(バグ)なんて出さないように組めば良いんでしょ」

「何かあったらちゃちゃっと直せば良いんでしょ、別にnullでシステムは落ちないよ(スクリプトの話)」

「それより、サービスのリリースの方が大切でしょ」

という雰囲気はあります。

今の時代はリリースの手早さは本当に大事なんです。

動的片付け言語の登場は時代に即してるんですよね。
山本 聡さんのプロフィール写真
山本 聡
·
フォロー
プログラマー : プログラミング研究家 : StandardSoftware株式会社 代表6ヶ月
関連
JavaScript でnullはどんな場面で使われますか?
このリンク先の画像で語られることが多いですね。有名な画像です。

What is the difference between null and undefined in JavaScript? (https://stackoverflow.com/questions/5076944/what-is-the-difference-between-null-and-undefined-in-javascript)

※あれ?StackOverFlowのURLが正常にリンクできないや…


何もないことを表すのがnullで、何もないことを表すことすら忘れてしまっているのがundefinedなので、undefined は変数などに代入はしないけど、nullは代入するとか、そういう方向でコード書く方針もあります。

また、nullを使わないという判断をする方針をとる場合もあるようです。

これはTSですがこちらに詳しくのっています。

undefinedとnullの違い | TypeScript入門『サバイバルTypeScript』
多くのプログラミング言語で「値がない」を表現する方法は、nullなど1通りです。しかし、JavaScriptでは「値がない」に相当する表現にnullとundefinedの2通りがあります。他の言語からJavaScriptに来た人が驚き、使い分けに悩む部分です。ここでは、nullとundefinedの仕様上の違い、実際のコーディングでどう使い分けるべきかについて説明します。
https://typescriptbook.jp/reference/values-types-variables/undefined-vs-null
閲覧数:2,205回
20件の高評価を見る

0 件のコメント:

コメントを投稿