第3版 が利用可能です。 こちらをお読みください!

第12章
JavaScriptとブラウザ

ブラウザは非常に敵対的なプログラミング環境です。

ダグラス・クロックフォード, The JavaScript Programming Language (ビデオレクチャー)

この本の次のパートでは、Webブラウザについて説明します。Webブラウザがなければ、JavaScriptは存在しなかったでしょう。たとえ存在したとしても、誰も関心を示さなかったでしょう。

Webテクノロジーは、当初から、技術的にだけでなく、進化の過程においても分散型でした。さまざまなブラウザベンダーが、場当たり的で、時には考え抜かれていない方法で新しい機能を追加し、それが他のベンダーに採用され、最終的に標準として確立されることがありました。

これは恩恵であると同時に呪いでもあります。一方で、中央の組織がシステムを制御するのではなく、緩やかな協力(または、時には公然とした敵対)の中でさまざまな組織によって改善されていくことは、力となります。他方で、Webが開発された場当たり的な方法は、結果として得られたシステムが内部整合性の輝かしい例ではないことを意味します。実際、その一部は非常に混乱していて、わかりにくいものです。

ネットワークとインターネット

コンピュータネットワークは1950年代から存在しています。2台以上のコンピュータ間にケーブルを接続し、これらのケーブルを通じてデータをやり取りできるようにすれば、あらゆる素晴らしいことができます。

同じ建物内の2台のマシンを接続することで素晴らしいことができるのであれば、地球全体のマシンを接続すればさらに良いはずです。このビジョンを実装するためのテクノロジーは1980年代に開発され、その結果として生まれたネットワークがインターネットです。それはその約束を果たしてきました。

コンピュータは、このネットワークを使用して、別のコンピュータにビットを吐き出すことができます。このビット吐き出しから効果的な通信が生まれるためには、両端のコンピュータは、そのビットが何を表しているのかを知っている必要があります。与えられたビット列の意味は、それが表現しようとしているものの種類と使用されているエンコードメカニズムに完全に依存します。

ネットワークプロトコルは、ネットワークを介した通信のスタイルを記述します。メールの送信、メールの取得、ファイルの共有、さらには悪意のあるソフトウェアに感染したコンピュータの制御のためのプロトコルがあります。

たとえば、単純なチャットプロトコルは、あるコンピュータがテキスト「CHAT?」を表すビットを別のマシンに送信し、相手が「OK!」でプロトコルを理解したことを確認するもので構成されるかもしれません。その後、お互いにテキスト文字列を送信したり、ネットワークから相手が送信したテキストを読み取ったり、受信したものを画面に表示したりすることができます。

ほとんどのプロトコルは、他のプロトコルの上に構築されています。私たちのチャットプロトコルの例では、ネットワークを、ビットを投入すると正しい順序で正しい宛先に到着するストリームのようなデバイスとして扱います。これらのことを保証するのは、すでにかなり困難な技術的な問題です。

Transmission Control Protocol (TCP) は、この問題を解決するプロトコルです。すべてのインターネット接続デバイスはこれを「話す」ことができ、インターネット上のほとんどの通信は、その上に構築されています。

TCP接続は次のように機能します。1台のコンピュータは、他のコンピュータが自分に話し始めるのを待っている、つまりリッスンしている必要があります。1台のマシンで同時に異なる種類の通信をリッスンできるようにするために、各リスナーには(ポートと呼ばれる)番号が関連付けられています。ほとんどのプロトコルは、デフォルトで使用されるポートを指定します。たとえば、SMTPプロトコルを使用してメールを送信する場合、送信に使用するマシンはポート25でリッスンしていることが期待されます。

別のコンピュータは、正しいポート番号を使用してターゲットマシンに接続することにより、接続を確立できます。ターゲットマシンに到達可能で、そのポートでリッスンしている場合、接続は正常に作成されます。リッスンしているコンピュータはサーバーと呼ばれ、接続しているコンピュータはクライアントと呼ばれます。

このような接続は、ビットが流れる双方向パイプとして機能します。両端のマシンは、データをそこに入れることができます。ビットが正常に送信されると、反対側のマシンによって再び読み取ることができます。これは便利なモデルです。TCPはネットワークの抽象化を提供していると言えるでしょう。

Web

World Wide Web(インターネット全体と混同しないように)は、ブラウザでWebページを閲覧できるようにする一連のプロトコルとフォーマットです。名前の「Web」の部分は、そのようなページが互いに簡単にリンクできるため、ユーザーが移動できる巨大なメッシュに接続できることを指します。

Webにコンテンツを追加するには、インターネットにマシンを接続し、Hypertext Transfer Protocol(HTTP)を使用してポート80でリッスンさせるだけです。このプロトコルを使用すると、他のコンピュータがネットワーク経由でドキュメントを要求できます。

Web上の各ドキュメントは、次のようなUniform Resource Locator(URL)によって名前が付けられます。

  https://eloquentjavascript.dokyumento.jp/12_browser.html
 |      |                      |               |
 protocol       server               path

最初の部分は、このURLがHTTPプロトコルを使用していることを示しています(たとえば、暗号化されたHTTPであるhttps://とは対照的です)。次に、どのサーバーからドキュメントを要求しているかを識別する部分が続きます。最後は、目的の特定のドキュメント(またはリソース)を識別するパス文字列です。

インターネットに接続された各マシンは、37.187.37.82のような一意のIPアドレスを取得します。これらはURLのサーバー部分として直接使用できます。しかし、多かれ少なかれランダムな数のリストは覚えにくく、入力しにくいため、代わりに特定の1台または複数のマシンを指すようにドメイン名を登録できます。私は、自分が制御するマシンのIPアドレスを指すようにeloquentjavascript.netを登録し、そのドメイン名を使用してWebページを提供することができます。

上記のURLをブラウザのアドレスバーに入力すると、ブラウザはそのURLにあるドキュメントを取得して表示しようとします。まず、ブラウザはeloquentjavascript.netがどのアドレスを指しているかを調べる必要があります。次に、HTTPプロトコルを使用して、そのアドレスにあるサーバーに接続し、リソース/12_browser.htmlを要求します。

HTTPプロトコルについては、第17章で詳しく見ていきます。

HTML

Hypertext Markup Languageの略であるHTMLは、Webページに使用されるドキュメント形式です。HTMLドキュメントには、テキストだけでなく、リンク、段落、見出しなどのテキストに構造を与えるタグも含まれています。

簡単なHTMLドキュメントは次のようになります。

<!doctype html>
<html>
  <head>
    <title>My home page</title>
  </head>
  <body>
    <h1>My home page</h1>
    <p>Hello, I am Marijn and this is my home page.</p>
    <p>I also wrote a book! Read it
      <a href="https://eloquentjavascript.dokyumento.jp">here</a>.</p>
  </body>
</html>

山かっこ(<および>)で囲まれたタグは、ドキュメントの構造に関する情報を提供します。その他のテキストは単なるプレーンテキストです。

ドキュメントは<!doctype html>で始まり、過去に使用されていたさまざまな方言とは対照的に、ブラウザにモダンHTMLとして解釈するように指示します。

HTMLドキュメントには、headとbodyがあります。headにはドキュメントに関する情報が含まれており、bodyにはドキュメント自体が含まれています。この例では、最初にこのドキュメントのタイトルが「My home page」であることを宣言し、次に(「見出し1」を意味する<h1><h2>から<h6>まではより小さな見出しを生成します)見出しと2つの段落(<p>)を含むドキュメントを与えました。

タグにはいくつかの形式があります。body、段落、リンクなどの要素は、<p>のような開始タグで始まり、</p>のような終了タグで終わります。リンク(<a>)のタグなど、一部の開始タグには、name="value"ペアの形式で追加情報が含まれています。これらは属性と呼ばれます。この場合、リンクの宛先はhref="https://eloquentjavascript.dokyumento.jp"で示されており、hrefは「ハイパーテキスト参照」を表します。

一部の種類のタグは何も囲んでいないため、閉じる必要はありません。この例としては、<img src="http://example.com/image.jpg">があります。これは、指定されたソースURLにある画像を表示します。

HTMLで特別な意味を持つにもかかわらず、ドキュメントのテキストに山かっこを含めることができるようにするために、別の形式の特殊表記法を導入する必要があります。単純な開始山かっこは&lt;(「より小さい」)として記述され、終了かっこは&gt;(「より大きい」)として記述されます。HTMLでは、アンパサンド(&)文字の後に単語とセミコロン(;)が続くものはエンティティと呼ばれ、エンコードされた文字に置き換えられます。

これは、JavaScript文字列でバックスラッシュが使用される方法に似ています。このメカニズムによってアンパサンド文字も特別な意味を持つため、これらは&amp;としてエスケープする必要があります。二重引用符で囲まれた属性内では、&quot;を使用して実際の引用符文字を挿入できます。

HTMLは、非常に寛容な方法で解析されます。存在すべきタグがない場合、ブラウザはそれらを再構築します。これが行われる方法は標準化されており、すべての最新のブラウザが同じように行うことを信頼できます。

以下のドキュメントは、以前に示したものと全く同様に扱われます。

<!doctype html>

<title>My home page</title>

<h1>My home page</h1>
<p>Hello, I am Marijn and this is my home page.
<p>I also wrote a book! Read it
  <a href=https://eloquentjavascript.dokyumento.jp>here</a>.

<html><head>、および<body>タグは完全に削除されています。ブラウザは、<title>がheadに属し、<h1>がbodyに属していることを認識しています。さらに、新しい段落を開いたりドキュメントを終了したりすると暗黙的に段落が閉じられるため、段落を明示的に閉じることはなくなりました。リンクターゲットの周りの引用符も削除されています。

この本では通常、例を短くして煩雑さを避けるために、<html><head>、および<body>タグを省略します。しかし、タグは閉じ、属性の周りには引用符を含めます。

また、通常はDOCTYPEも省略します。これはDOCTYPE宣言を省略することを推奨するものではありません。ブラウザは、DOCTYPEを忘れると、しばしばとんでもないことをします。例では、実際にテキストに表示されていなくても、DOCTYPEは暗黙的に存在すると考えてください。

HTMLとJavaScript

本書の文脈において、最も重要なHTMLタグは<script>です。このタグを使用すると、ドキュメントにJavaScriptのコードを含めることができます。

<h1>Testing alert</h1>
<script>alert("hello!");</script>

このようなスクリプトは、ブラウザがHTMLを読み込む際に、その<script>タグに遭遇するとすぐに実行されます。先ほど示したページは、開くとalertダイアログをポップアップ表示します。

大規模なプログラムをHTMLドキュメントに直接含めるのは、しばしば現実的ではありません。<script>タグには、URLからスクリプトファイル(JavaScriptプログラムを含むテキストファイル)をフェッチするために、src属性を指定できます。

<h1>Testing alert</h1>
<script src="code/hello.js"></script>

ここに含めたcode/hello.jsファイルには、同じ単純なプログラムalert("hello!")が含まれています。HTMLページが、イメージファイルやスクリプトなど、自身の一部として他のURLを参照すると、Webブラウザはそれらを即座に取得し、ページに含めます。

スクリプトタグは、スクリプトファイルを参照していて、コードが含まれていない場合でも、必ず</script>で閉じる必要があります。これを忘れると、ページの残りの部分がスクリプトの一部として解釈されます。

一部の属性には、JavaScriptプログラムを含めることもできます。次に示す<button>タグ(ボタンとして表示されます)には、onclick属性があり、その内容はボタンがクリックされるたびに実行されます。

<button onclick="alert('Boom!');">DO NOT PRESS</button>

onclick属性内の文字列には、二重引用符が属性全体を引用するためにすでに使用されているため、一重引用符を使用する必要があったことに注意してください。&quot;を使用することもできましたが、プログラムが読みにくくなります。

サンドボックス内

インターネットからダウンロードしたプログラムを実行することは潜在的に危険です。訪問するほとんどのサイトの背後にいる人々についてあまり知らず、彼らは必ずしも善意であるとは限りません。善意でない人々のプログラムを実行すると、コンピューターがウイルスに感染したり、データが盗まれたり、アカウントがハッキングされたりする可能性があります。

しかし、Webの魅力は、訪問するすべてのページを必ずしも信頼しなくてもサーフィンできることです。そのため、ブラウザはJavaScriptプログラムができることを厳しく制限しています。それは、コンピュータ上のファイルを見たり、埋め込まれたWebページに関連しないものを変更したりすることはできません。

このようにプログラミング環境を隔離することをサンドボックスと呼びます。これは、プログラムが無害にサンドボックスで遊んでいるという考えに基づいています。ただし、この特定の種類のサンドボックスは、厚い鋼鉄の棒で覆われたケージを持っていると想像する必要があります。これは、典型的な遊び場のサンドボックスとは少し異なります。

サンドボックスの難しい点は、プログラムが役立つように十分な余地を与えながら、危険なことをしないように制限することです。他のサーバーとの通信やコピーペーストクリップボードの内容の読み取りなど、多くの便利な機能は、プライバシーを侵害する問題のあることに使用することもできます。

時々、誰かがブラウザの制限を回避して、軽微な個人情報の漏洩からブラウザが実行されているマシン全体の乗っ取りまで、有害な何かをする新しい方法を思いつきます。ブラウザの開発者は穴を修正することで対応し、すべてが再びうまくいきます。つまり、次の問題が発見され、政府やマフィアによって密かに悪用されるのではなく、うまくいけば公開されるまでです。

互換性とブラウザ戦争

Webの初期段階では、Mosaicというブラウザが市場を支配していました。数年後、バランスはNetscapeに移り、その後、MicrosoftのInternet Explorerに大きく取って代わられました。単一のブラウザが優勢であったどの時点でも、そのブラウザのベンダーはWebの新しい機能を一方的に発明する権利があると考えるでしょう。ほとんどのユーザーが同じブラウザを使用していたため、Webサイトは他のブラウザを無視して、これらの機能を使い始めるだけでした。

これは互換性の暗黒時代であり、しばしばブラウザ戦争と呼ばれます。Web開発者は、単一の統合されたWebではなく、2つまたは3つの互換性のないプラットフォームに置き去りにされました。さらに悪いことに、2003年頃に使用されていたブラウザはすべてバグだらけであり、もちろんバグはブラウザごとに異なりました。Webページを作成する人々にとって、生活は困難でした。

Netscapeの非営利組織であるMozilla Firefoxは、2000年代後半にInternet Explorerの覇権に挑戦しました。当時、マイクロソフトは競争力を維持することに特に関心がなかったため、Firefoxは市場シェアのかなりの部分を奪い取りました。ほぼ同時期に、GoogleはChromeブラウザを導入し、AppleのSafariブラウザの人気が高まり、1つではなく4つの主要なプレーヤーが存在する状況になりました。

新しいプレーヤーは、標準に対してより真剣な姿勢を持ち、より優れたエンジニアリングプラクティスを採用したため、非互換性が減り、バグも少なくなりました。市場シェアが崩れるのを見たマイクロソフトは、方向転換してこれらの姿勢を採用しました。今日、Web開発を学び始めているのであれば、あなたは幸運だと思ってください。主要なブラウザの最新バージョンは、かなり均一に動作し、バグは比較的少ないです。

だからといって、状況がまだ完璧だというわけではありません。Webを使用している一部の人々は、慣性や企業の方針などの理由で、非常に古いブラウザに行き詰まっています。これらのブラウザが完全に消滅するまで、それらで動作するWebサイトを作成するには、それらの欠点や癖に関する多くの秘伝の知識が必要になります。この本は、これらの癖についてではありません。むしろ、最新の、健全なWebプログラミングのスタイルを提示することを目的としています。