null or undefined #kyotoasterisk とその補足など

Kyoto.なんか #6 で発表しました.

speakerdeck.com

以下はその補足情報など.

仕様書中の出現頻度

null と undefined がそれぞれの仕様でどの程度使われているのかは, 仕様書中の出現頻度を見るだけでもある程度わかりりそうです. ということで ECMAScript 2024WHATWG の標準 (2024-09-07 時点) のうちいくつかの仕様書の中での出現頻度を見てみましょう.

Spec #null #undefined
ES2024 300 939
DOM 368 120
Fetch 276 10
HTML 1778 275
URL 98 9

見ての通り, 顕著に登場頻度に差があることがわかりますね.

なお上記の null の出現数には WebIDL の nullable (T?) を含めていないため, Web 標準における実際の null の登場頻度はもっと多くなるはずです.

仕様書の null 全部読む

上記の通り ES2024 の仕様には null が 300 回しか登場しません. この程度なら null の登場箇所は全部読めるなと思ったので読みました.

いくつか手元のメモから面白かった箇所を抜粋します:

  • null がオブジェクトの prototype となるのは, プログラマがそうした場合を除くとたぶん以下のいずれか
  • Date.prototype.toJSON は時刻が finite でないとき null を返す
  • String.prototype.matchAll が返すイテレータは null を返す的なことが書いてあるが, たぶん普通はそんなことはない
  • Array.prototype.join は配列中の null や undefined を無視する
  • Object.assign は引数中の null や undefined を無視する
  • Map, Set, WeakMap, WeakSet のコンストラクタは引数に null や undefined が渡されると無視する

ECMAScript の言語仕様での例外的なケースについて

スライド中でも紹介した通り, ECMAScript の言語仕様中で null が使われるのは (仕様内部での利用を除けば) 以下の 3 つです.

このうち JSON で null が使われる理由は明白で, JSON の仕様でそう定義されているためです.

正規表現についてはおそらく歴史的な事情で, 今となっては特に深い意味はないものと思われます. (この理由に関する情報を昔見かけたことがある気もしますが, 真偽が定かでないのでここで広めるのはやめておきます.)

プロトタイプについては, ES5 より前には仕様内部で用いられていたものが, ES5 以降に Object.getPrototypeOf などが追加されたことで正式に表に出てくるようになったものと考えています.

Web 標準仕様で例外的なケースについて

例えば CustomElementRegistry#get が custom element が見つからない時に undefined を返すようです.

オブジェクトや文字列に対しては null を使うのでは?

文字列は JavaScript ではプリミティブ値の一種ですが, 他の言語ではオブジェクト側に分類されることもあります. 実際, ECMAScript の言語仕様における例外ケースや Web 標準の慣例は, こういった他の言語の仕様や慣習に由来するものでほぼ間違いないでしょう.

一方, その他のプリミティブ値に対してであったり, プリミティブ値とオブジェクトが混在する場合に null と undefined のどちらを使うのかは, これだけでは説明できないはずです. ECMAScript の言語仕様でもこういった場合に null を使っても良いはずですが, 原則 undefined を使うように統一されています.

また TypeScript を使う場合に Map<string, string> と型を制限したからといって get() したときに null が返るかというとそんなことはないので, やはり出自も考えるほうが混乱しないでしょう.