第4版が利用可能です。こちらで読んでください

第13章JavaScriptとブラウザ

Webの背後にある夢は、情報を共有することでコミュニケーションを図る共通の情報空間です。その普遍性は不可欠です。ハイパーテキストリンクが、個人的なもの、ローカルなもの、グローバルなもの、草稿、あるいは高度に洗練されたものなど、あらゆるものを指し示すことができるという事実は重要です。

ティム・バーナーズ=リー, The World Wide Web: A very short personal history
Picture of a telephone switchboard

この本の次の章では、Webブラウザについて説明します。Webブラウザがなければ、JavaScriptは存在しなかったでしょう。たとえ存在したとしても、誰もそれに注目することはなかったでしょう。

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

これは祝福でもあり、呪いでもあります。一方では、中央の機関がシステムを制御するのではなく、緩やかな協力(または時には公然とした敵意)の中で働くさまざまな関係者によって改善されることは、力を与えます。他方では、Webが開発された行き当たりばったりな方法の結果、出来上がったシステムは、内部の一貫性の輝かしい例とは言えません。その一部は、まったくもって混乱していて、うまく考えられていません。

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

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

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

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

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

たとえば、Hypertext Transfer Protocol(HTTP)は、名前付きリソース(Webページや画像などの情報のかたまり)を取得するためのプロトコルです。リクエストを行う側が、リソースの名前と使用しようとしているプロトコルのバージョンを次のような行で始める必要があることを指定します。

GET /index.html HTTP/1.1

リクエスタがリクエストにさらに情報を含める方法や、リソースを返す側のコンテンツをパッケージ化する方法について、さらに多くのルールがあります。HTTPについては、18章で詳しく見ていきます。

ほとんどのプロトコルは、他のプロトコルの上に構築されています。HTTPは、ネットワークをビットを投入でき、正しい宛先に正しい順序で届くストリームのようなデバイスとして扱います。11章で見たように、これらのことを保証することはすでにかなり難しい問題です。

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

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

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

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

Web

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

Webの一部になるために必要なことは、マシンをインターネットに接続し、他のコンピュータがドキュメントを要求できるように、HTTPプロトコルでポート80をリスニングさせることだけです。

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

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

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

インターネットに接続されたマシンは、そのマシンにメッセージを送信するために使用できる数値であるIPアドレスを取得します。これは149.210.142.2192001:4860:4860::8888のように表示されます。しかし、多かれ少なかれランダムな数字のリストは覚えにくく、入力しにくいため、代わりに特定のアドレスまたはアドレスのセットにドメイン名を登録できます。私は、私が制御するマシンのIPアドレスを指すようにeloquentjavascript.netを登録し、そのドメイン名を使用してWebページを配信することができます。

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

HTML

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

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

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <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にはドキュメント自体が含まれています。この場合、headは、このドキュメントのタイトルが「私のホームページ」であり、バイナリデータとしてUnicodeテキストをエンコードする方法であるUTF-8エンコーディングを使用していることを宣言しています。ドキュメントのbodyには、見出し(<h1>、「見出し1」を意味する—<h2>から<h6>は小見出しを生成します)と2つの段落(<p>)が含まれています。

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

一部の種類のタグは何も囲まないため、閉じる必要はありません。メタデータタグ<meta charset="utf-8">がその例です。

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

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

HTMLは、非常にエラー耐性のある方法で解析されます。そこにあるはずのタグが欠落している場合、ブラウザはそれらを再構築します。これがどのように行われるかは標準化されており、すべての最新ブラウザが同じように行うことを信頼できます。

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

<!doctype html>

<meta charset=utf-8>
<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>タグは完全に削除されています。ブラウザは、<meta><title>がheadに属し、<h1>がbodyが開始されたことを意味すると認識します。さらに、新しい段落を開始するかドキュメントを終了すると暗黙的に閉じられるため、段落を明示的に閉じることはもうありません。属性値の周りの引用符も削除されています。

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

また、通常は doctype と charset 宣言も省略します。これは、これらの要素をHTMLドキュメントから削除することを推奨するものではありません。ブラウザは、これらを忘れると、しばしばとんでもないことをします。たとえテキストに実際に示されていなくても、doctypeとcharsetメタデータは例の中に暗黙的に存在すると考えるべきです。

HTMLとJavaScript

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

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

このようなスクリプトは、ブラウザがHTMLを読み込んでいる間に<script>タグに出会うとすぐに実行されます。このページを開くとダイアログが表示されます。alert関数はpromptに似ており、小さなウィンドウが表示されますが、入力は求めずにメッセージのみを表示します。

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

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

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

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

スクリプトタグにtype="module"属性を与えることで、ブラウザでESモジュール(第10章を参照)をロードできます。このようなモジュールは、import宣言でモジュール名として自分自身に対する相対URLを使用して、他のモジュールに依存することができます。

一部の属性には、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の地位に挑戦しました。当時、Microsoftは競争力を維持することに特に関心がなかったため、Firefoxはそこから多くの市場シェアを奪いました。ほぼ同時期に、GoogleはChromeブラウザを導入し、AppleのSafariブラウザが人気を博し、1つではなく4つの主要なプレーヤーが存在する状況になりました。

新しいプレーヤーは、標準に対するより真剣な姿勢とより優れたエンジニアリングプラクティスを持っており、互換性の低下とバグの減少につながりました。市場シェアが崩れるのを見たMicrosoftは、態度を改め、Internet Explorerに代わるEdgeブラウザでこれらの姿勢を採用しました。今日Web開発を学び始める人は、幸運だと考えてください。主要なブラウザの最新バージョンは非常に均一に動作し、バグも比較的少なくなっています。