今日はエープリルフールなので、JavaScriptに関する、にわかに信じがたい話(実話)をしたいと思います。実用的な話ではないので、息抜きがてらお読みいただき、「嘘だろ!?」とツッコミながらJavaScriptへの関心を少しでも深めていただければと思います。
思いつきでゆるめにとりとめもなく書いたため、内容がざっくりしているところがあります。詳しい方はコメントなどで補足いただけると助かります🙇🏻♂️ できるだけ十分に調査したつもりですが誤りなどあればご指摘いただければ幸いです。
JavaScriptは10日で作られました
JavaScriptは最も利用される言語のひとつで、JetBrainsの統計によれば70%近くの開発者が使ったことがあるほど、広く普及している言語です。
ここまで普及したとなると、JavaScriptはどれほど練りに練って、じっくり作り込まれた言語だったのでしょうか?
1995年、新進気鋭のブラウザメーカーだったNetscape社は、これまで静的だったHTMLを動的にできるようにする必要性を感じていました。当時、すでにJavaがあったため、本格的なウェブアプリケーションはJavaで実装することができました。
しかし、Javaはどちらかというとプロのプログラマー向けの言語というイメージがあったため、Netscape社はJavaよりもライトな言語で、例えばデザイナーがHTMLをちょっと動的にできるような言語を作ろうと考えていました。
そこで白羽の矢がたったのが、JavaScriptの生みの親であるブレンダン・アイク氏です。Netscape社が彼に伝えた要求は次のようなものでした。
- Javaっぽい言語で (当時Javaが脚光を浴びていた)
- でも、Javaっぽすぎないライトな言語を (独自性はほしい)
- 10日で作って欲しい。
10日でJavaっぽい言語を作るなんて依頼が来たら、お断りしそうな案件ですが、ブレンダン・アイク氏はこれをやり遂げてしまいます。
アイク氏は、学生時代から新しい言語を多数作ってきていて、新しい言語を作るのは得意だったそうですが、そうだとしても天才としか思えません。そして、わずか10日でリリースされた言語が、いろいろありながらも、ここまで普及したのを目の当たりにすると、まるで創生神話でも聞いているようです。
Dateの不評はJavaのせいだった!?
JavaScriptで最も使いにくいビルトインAPIと言えば、Date
オブジェクトではないでしょうか。
Dateの設計はとても簡素で、yyyy年m月d日といったシンプルな書式に出力するにしてもそこそこコードを書かないとなりません。
const d = new Date();
const year = d.getFullYear();
const month = d.getMonth() + 1;
const day = d.getDate();
console.log(`${year}年${month}月${day}日`);
他にもDateにはタイムゾーンが無かったりもします。
使い勝手の悪さから、Moment.jsやdate-fnsといったサードパーティの日付ライブラリにお世話になった人も多いのではないでしょうか。あまりの不評もあって、Temporalというモダンな日付ビルトインAPIも検討されはじめています。
ところで、Dateはどうしてこんなに微妙な実装になっているのでしょうか。これにも歴史が関係します。JavaScriptを実装するのにブレンダン・アイク氏に与えられたのは、わずか10日だったことは上でお伝えしました。この工期には日付処理の実装も含まれていました。とはいえ、日付処理は複雑でフルスクラッチで作ったら時間がかかるものです。工期短縮のため、実装は当時のJavaのjava.util.Date
から移植されることになりました1。
Javaの日付処理を知っている人なら「由来なら、どうしてこんなひどい実装になったんだ!?」と思われるかもしれません。今のJavaの日付処理はとても立派なものです。実は、当時のJavaのjava.util.Date
は今のJavaの日付処理とは、全然異なるものだったのです。それは評判の良くないものだったそうです。
当時のJava(1.0系)のjava.util.Date
のドキュメント。見てみると、たしかにJavaScriptのDateとインターフェイスがそっくりです。移植されたというのが、ここからも垣間見えます。
あまりにもそっくり移植されてしまったため、2000年問題のバグまで引き継いでしまったそうです。
https://www.mozilla.org/js/language/ICFP-Keynote.ppt
console.log(new Date(1999, 3, 1).getYear());
//=> 99
console.log(new Date(2000, 3, 1).getYear());
//=> 100
バグと言いましたが、今となっては仕様になっているんですけどね…。
Javaでは1.0のjava.util.Date
が良くなかったため、Java 1.1のリリースですぐに非推奨になり、新しい日付処理に置き換えられていきました。その結果、Javaの方は十分に使いやすい日付処理になっています。
Javaを真似したJavaScriptはその後どうなったかというと…
20年以上Dateはそのままです。これだけは本当に嘘であってほしかったと思う事実です。
JavaScriptがプロトタイプベースは納期都合?
クラスベースのオブジェクト指向を採用している言語を学んでから、JavaScriptを学ぶときに驚くのが、JavaScriptは「プロトタイプベース」というものを採用している点です。プロトタイプベースの詳細は次の投稿を見ていただきたいのですが、
ここでは「プロトタイプベースはクラスベースの言語とまったく考え方が異なる」とざっくりと捉えてください。多くの言語がクラスベースを採用しているため、そうした言語からJavaScriptに来た人は、「プロトタイプは馴染みが薄い」、「とっつきにくい」「難しい」といった感想を持つ人も少なくありません。
JavaScriptが参考にしたJavaはクラスベースです。なのに、なぜJavaScriptはプロトタイプベースを採用したのでしょうか。
この採用理由にも、工期わずか10日という短納期が関係してきそうです。クラスベースの言語を作るのは、プロトタイプベースの言語を作るより難しいと言われています。JavaScriptを作るのに与えられた時間は非常に少ないものでしたから、工数削減にもプロトタイプベースは一役買ったことでしょう。JavaScriptの20年の歩みを詳しく記述した『JavaScript: The First 20 Years』では、クラスのサポートはJavaとの競合リスクがある上に、時間がかるため、プロトタイプベースを採用した見られる記述があります。
逆に、工期が十分に与えられていたら、JavaScriptはクラスベースになっていたのでしょうか。実はそうではないようです。JavaScriptの作者であるブレンダン・アイク氏は、後のインタビューで次のように語っています。
Seibel: So you wanted to be like Java, but not too much.
(Javaのようにしたいけれど、大掛かりはしたくなかったわけですね。)
Eich: Not too much. If I put classes in, I'd be in big trouble. Not that I really had time to, but that would've been a no-no.
(そうですね。もしクラスを取り入れていたら、大変なことになっていたでしょう。時間がなかったのは確かですが、時間があったとしてもクラスはいやですね。)
アイク氏は、JavaScriptを設計するにあたって、できるだけ言語をシンプルにしたいと考えていました。JavaScriptはSelfという言語の影響を受けていると言われています。Selfはプロトタイプベースです。そして、Selfのキャッチコピーは「The Power of Simplicity」つまり「シンプルさの力」です。プロトタイプベースのほうが単純で柔軟なプログラミングができるいうのがSelfの主張です。アイク氏も、Selfの主張に賛同する形で、クラスよりもプロトタイプを選択したようです。
JavaScriptがプロトタイプベースを採用したのは、工期短縮の狙いもあるけど、Selfの理想に共感したというのが大きそうです。
昔JSでも型アノテーションが検討されていた
最近TypeScript開発チームから、JavaScriptにも型注釈を書けるようにする提案が提出されて、ちょっとしたニュースにもなりました。個人的にも、型注釈がJavaScriptに書けたらいいなとは思うので、是非進めていってほしいプロジェクトです。
TypeScriptや型注釈がある言語に馴染みのある人、すでに型注釈の導入が進んでるスクリプト言語を使っている人たちの中からは、「JavaScriptにもやっとスタートラインに立ったか」という反応も見かけます。しかし、JavaScriptで型注釈の導入が議論されたのは、今回が初めてではありません。
1999年から2008年にかけて、TC39というJavaScriptの標準化チームは、ECMAScript 4という次の言語仕様の策定に着手していました。数々の議論を経たのち、初稿ではJavaScriptに次の特徴を盛り込むところまで決まりました。
- 型注釈
- クラス、インターフェイス
- パッケージと名前空間、バージョニング機構
- private, internal, publicなどのアクセス修飾子
- int型やuint型など
せっかくなのでECMAScript 4でどのようなものが検討されていたかサンプルコードをいくつか見てみましょう。
変数名の右に型注釈が書けるようです。このままでも、TypeScriptで解釈できそうなくらいTypeScriptっぽいです。
var a: Object = undefined;
var b: Object = 3;
var c: Integer = 7;
クラス構文です。今のJSのクラス構文とは若干違いますね。
class Greeter {
var saying = "hello, world"
function hello() {
print(saying)
}
}
var greeter : Greeter = new Greeter
greeter.hello()
続いてインターフェイス。TypeScriptとはちょっと違いますが、だいぶ似ています。
interface Greetings {
function hello()
function goodmorning()
}
パッケージ構文とpublic
キーワード。
package actors {
public class Greeter {
public function hello() {
print("hello, world")
}
}
}
ちなみに、ES4とは別規格ですがE4XというXMLリテラルをJavaScriptに直接書けるJSXのような規格もあったそうです。
var employees = <employees>
<person>
<name>Tove</name>
<age>32</age>
</person>
<person>
<name>Jani</name>
<age>26</age>
</person>
</employees>;
document.write(employees.person.(name == "Tove").age);
こうしてサンプルコードを眺めてみると、TypeScriptに似ていたり、JavaScriptには導入されていない書き方が見られます。
結局、ECMAScript 4はどうなったかというと、紆余曲折あって、最終的に破棄されました。ECMAScript 4が承認された世界線があったら、TypeScriptは生まれていなかったかもしれませんね。
ちなみに、ECMAScript 4はJavaScriptには採用されなかったものの、Flash用の言語ActionScript 2.0 〜 3.0に採用されました。Flashも2020年12月31日にサポートが終了したため、ECMAScript 4に触れる機会は滅多になさそうです。
<!-- -->
はJavaScriptのコメント
JavaScriptのコメントといえば、//
や/* */
ですが、実はHTML風のコメント<!--
と-->
もJavaScriptで有効なコメントです。なので、JavaScriptコード中に<!--
や-->
を書いても、構文エラーにはなりません。
const a = 1; <!-- コメント
このHTML風コメントは、JavaScriptの仕様であるECMAScript(ECMA262)でも「HTML-like Comments」として規定されています。この仕様によれば、<!--
と-->
は行コメントということになっています。要するに//
相当ということです。したがって、複数行にまたがってHTML風コメントを使うことはできません。この点はHTMLのコメントとは異なる仕様です。
<!--
console.log("hello world"); // この行は実行される
-->
そもそも、なぜHTML風コメントがJavaScriptの仕様の一部になっているかというと、歴史的な経緯が関係しています。
かつてJavaScriptをサポートしていないブラウザは<script>
要素を解釈できないため、JavaScriptコードがウェブサイトにそのまま表示されてしまう問題がありました。この問題を回避するために、<script>
タグ内は一度HTMLコメントにするという手法が広まりました。
<script type="text/javascript">
<!--
alert("hello world");
-->
</script>
今となっては実務上このような処置をする必要はまず無いですが、ECMAScriptは後方互換性を重視しているので、こうした歴史的経緯も仕様としてしっかり盛り込んでいるのです。
.jsというトップレベルドメイン
トップレベルドメイン(TLD)は、github.comの.comやmozilla.orgの.orgのように、ドメイン名でドットで区切られた文字列の一番右の部分です。.jp(日本)など地域系から、.ninja(忍者)や.moe(萌え)など面白い系のものまでいろいろあります。
JavaScriptの拡張子.jsと同じトップレベルドメインを実現しようというクラウドファンディングプロジェクトが、かつて存在していたのはご存知でしょうか。このプロジェクトでは、JavaScriptハッカーが管理するTLDが有ってもいいのではないかというところからスタートしています。
TLD.jsが実現された場合、jQueryにはjquery.jsを、Node.jsにはnode.jsといった具合に、プロジェクトに即したドメインが分配するつもりだったようです。他にも min.js ドメインはCDN的に提供するなど、ちょっと便利そうなアイディアも出ていました。
$180,000集まれば、TLDの申請ができるということで資金提供を募ったのですが、二文字のTLDは国レベルでないと申請できないというツッコミがありました。
これに対し、「ならば国を作ろう」という提案がありましたが、その後、無事に建国できたかは定かでありません。
MDNには「超電磁砲」が載っている
JavaScriptに携わっているとMDNのリファレンスにはよくお世話になるのではないでしょうか。MDNでは、JavaScriptだけでなくCSSについても豊富なドキュメントがあります。
その中でも、CSSのruby-positionのページには、唐突に「超電磁砲」がサンプルとして登場します。
超電磁砲と書いてレールガンと読むのは、累計発行部数は680万部を記録する日本の人気漫画作品『とある科学の超電磁砲』以外に考えられないです。きっとこのページを書いた人は日本の方なんでしょう。そう思い調べてみたところ、スイスの方でした。数ある単語の中から、なぜこれを選んだのでしょうか。気になるところです。
-
という週2万ダウンロードを誇る謎のパッケージがある
NPMはJavaScriptのパッケージを配布する最も有名なプラットフォームです。React.jsやTypeScriptを導入する際には、ここからパッケージをとってきてインストールします。
# TypeScriptを導入する例
npm install typescript
NPMは無料で誰でも使えるので、有名ならパッケージから、個人がほそぼそと開発しているパッケージまで様々なものが配布されています。
NPMには、-というハイフン1文字だけのパッケージもあります。このパッケージは中身がほぼ空っぽで、何か機能を提供したりといったことはありません。
それなのに、週2万回ダウンロードされる人気ぶりです。そして、これに依存しているパッケージは180件以上あります。なぜこんなにインストールされているのか、一説によるとコマンドのタイプミスが考えられるそうです。
本来なら
npm install -D typescript
と打つべきところを、-とDの間にスペースを入れてしまって
npm install - D typescript
を実行してしまうと、typescriptと、Dと、-(ハイフン)の3つのパッケージが入ってしまいます。
NPM界隈では、特定のパッケージに悪意のあるマルウェアが混入される事件など、たびたびサプライチェーン攻撃が起こったりしますが、この謎パッケージは今のところ、特にそういった危険性はなさそうです。しかし、このパッケージのオーナー権限が、万が一悪意のある人に渡ってしまったらと考えると、ちょっと恐ろしいですね。。
JavaScriptにはfalsyなオブジェクトがある
falsyな値とは、if文の条件式や論理演算子||
の項などで偽(false)と判定される値を言います。JavaScriptでは、null
や空文字列、0
などがfalsyです。
true || console.log("falsy"); // 何も出力されない
null || console.log("falsy"); //=> falsy
0 || console.log("falsy"); //=> falsy
"" || console.log("falsy"); //=> falsy
JavaScriptでは、空のオブジェクトであっても、それはfalsyではありません。
const obj = {};
obj || console.log("falsy"); // 何も出力されない
obj && console.log("truthy"); //=> truthy
しかし、JavaScriptには常にfalsyになるオブジェクトがあります。それが、document.all
です。これは、HTMLAllCollection
を返すDOMのAPIです。
document.all
にはまだまだ特殊なところがあって、typeof
演算子をかけると"undefined"
が返ってきます。
typeof document.all === "undefined" // true
なぜこのような仕様になっているかというと、Internet Explorerかどうかを判定するために、document.all
がif文でよく使われてきたからのようです。
if (document.all) {
// Internet Explorerの処理
} else {
// それ以外
}
今となってはこんなコードは書かないと思いますが、JavaScriptは後方互換性を大事にしているので、こういう特殊な仕様が残っています。
\u0061
は変数名に使える
JavaScriptの変数名や関数名といった識別子には、使える文字と使えない文字、使えないキーワードがあるというのはご存知ではないでしょうか。
たとえば、a
は識別子として使えますが、バックスラッシュ\
は識別子に使えません。
let a = 123; // OK
let \ = 123; // 構文エラー
他には$
や日本語も識別子として使えます。
let $ = 123; // OK
let あ = 123; // OK
日本語も使えるのかと驚く人もいるかもしれませんが、これ以上にびっくりなのが\u0061
のようなユニコードエスケープシーケンスも識別子として使えることです。次のコードはちゃんと動きます。
let \u0061 = 123; // OK
console.log(a);
//=> 123
このlet \u0061
はlet a
と書くのと同じ意味になります。
エスケープシーケンスは中括弧を使った書き方も可能です。
let \u{61} = 123; // OK
console.log(a); //=> 123
この書き方は、1文字の識別子に限られません。たとえば、console
とlog
をエスケープシーケンスにするとこうなります。
\u{63}\u{6f}\u{6e}\u{73}\u{6f}\u{6c}\u{65}.\u{6c}\u{6f}\u{67}("OK");
//=> "OK"
これでも普通に動きます。
こうした書き方ができるとは言え、エスケープシーケンスで書かれた識別子はただただ読みにくいだけなので、絶対に真似しちゃだめです。
おわり
大変長い投稿になってしまいましたが、最後まで読んでくださりありがとうございました。
この機会にJavaScript関係の嘘のような話をいくつか紹介させていただきました。実用性のあるネタではありませんでしたが、少しでもJavaScriptに関心を持っていただけたら嬉しいです!
── PRなど ──
\クラフトマンソフトウェアではエンジニア募集中です!/
TypeScript入門『サバイバルTypeScript』も書いてます!無料なのでよかったらご覧ください。
『Software Design』の特集「JavaScriptの関数を極める」の一部を書きました。入門から発展までみっちり書かれているので、こちらも是非ご覧いただければと思います。
Twitterでは、Qiitaに書くほどでもないTipsやJavaScript関係のニュースを投稿してます!