JavaScript とブラウザ

ウェブの夢は、私たちが情報を共有することでコミュニケーションを行う共通の情報空間です。その普遍性は不可欠です。ハイパーテキストリンクが、個人的、ローカル、グローバル、草案、または非常に洗練されたものであっても、何でも指し示すことができるという事実です。

ティム・バーナーズ=リー、ワールドワイドウェブ:非常に短い個人的な評価
Illustration showing a telephone switchboard

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

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

これは一見両面性のあることです。一方では、中央の当事者がシステムを制御するのではなく、さまざまな当事者が緩やかな共同作業(またはオープンな敵意による場合もあります)によって改善されるため、力を与えることができます。一方で、Webが開発された無計画な方法により、結果として得られたシステムは内部的な一貫性の好例ではありません。その一部は全く混乱しており、設計が悪いです。

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

コンピュータネットワークは、1950年代から存在します。複数のコンピュータ間にケーブルを設置して、これらのケーブルを介してデータを相互に送受信できれば、素晴らしいことができます。

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

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

ネットワークプロトコルは、ネットワーク上でのコミュニケーションの形式を記述します。電子メールの送信、電子メールの取得、ファイルの共有、さらには悪意のあるソフトウェアに感染したコンピュータを制御するためのプロトコルがあります。

ハイパーテキスト転送プロトコル(HTTP)は、名前付きリソース(ウェブページや画像などの情報のかたまり)を取得するためのプロトコルです。リクエストを行う側は、この行から始め、リソースと使用しようとするプロトコルのバージョンを指定する必要があります

GET /index.html HTTP/1.1

リクエスト側は、リクエストにさらに情報を組み込む方法、およびリソースを返すもう一方の側がそのコンテンツをパッケージ化する方法は、他にも多数あります。HTTPの詳細については、第18章で説明します。

ほとんどのプロトコルは、他のプロトコル上に構築されます。HTTP はネットワークを、ビットを投入して正しい宛先へ正しい順番で届けられる、ストリーム状のデバイスとして扱います。ネットワークが提供する、原始的なデータ送信機能の上にそれらの保証を提供することは、すでにかなりやっかいな問題です。

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

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

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

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

Web

ワールドワイドウェブ (全体としてのインターネットと混同しないこと) は、ブラウザで Web ページを訪れることができるプロトコルと形式のセットです。Web という言葉は、これらのページはたやすく相互にリンクすることができることに由来しており、それによりユーザーが移動できる巨大なメッシュにつながります。

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

Web 上の各ドキュメントは、次のようなユニフォームリソースロケーター (URL) で名前が付けられています。

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

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

インターネットに接続されたマシンは、そのマシンにメッセージを送信するために使用できる数字の IP アドレス を取得し、149.210.142.219 または 2001:4860:4860::8888 のように見えます。多少ランダムな数字のリストを記憶したり、入力したりするのは難しいので、代わりにアドレスまたは一連のアドレスの ドメイン名 を登録できます。私は eloquentjavascript.net を制御するマシンの IP アドレスを指すように登録し、そのドメイン名を使用してウェブページを提供できます。

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

HTML

HTML は HyperText Markup Language の略であり、ウェブページに使用されるドキュメントの形式です。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 ドキュメントには、ヘッダーと本文があります。ヘッダーにはドキュメント に関する 情報が含まれ、本文にはドキュメント自体が含まれます。この場合、ヘッダーはこのドキュメントのタイトルを「マイホーム」とし、ユニコードテキストをバイナリデータとしてエンコードする方法である UTF-8 エンコーディングを使用することを宣言します。ドキュメントの本文には、見出し (<h1> は「見出し 1」を意味します。サブ見出しを作成するには <h2> から <h6> を使用します) と 2 つの段落 (<p>) が含まれます。

タグにはいくつかの形式があります。本文、段落、またはリンクなどの要素は、<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> タグには src 属性を指定して、URL からスクリプトファイル(JavaScript プログラムを含むテキストファイル)を取得できます。

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

ここに含まれる code/hello.js ファイルには、alert("hello!") という同じプログラムが含まれています。HTML ページは、画像ファイルやスクリプトなど、他の URL を参照すると、ウェブブラウザはそれらをただちに取り出してページに挿入します。

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

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

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

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

onclick 属性の文字列には一重引用符を使用する必要があることに注意してください。二重引用符は属性全体を引用するためにすでに使用されています。内部の引用符をエスケープするために &quot; を使用することもできます。

サンドボックス内

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

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

このようにプログラミング環境を分離することを「サンドボックス」と呼びます。プログラムがサンドボックス内で無害に動作するという考え方です。ただし、この特定のタイプのサンドボックスは、中で動作するプログラムが実際に抜け出せないように、分厚い鉄の棒で覆われたケージのようなものをイメージする必要があります。

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

時折、誰かがブラウザの制限を回避し、ブラウザが動作するマシン全体を乗っ取ることから、マイナーな個人情報の漏洩に至るまでの有害なことを行う新しい方法を考え出します。ブラウザの開発者は穴を塞ぐことで対応し、問題は解決します。ただし、次の問題が発生するまでは問題ありません。問題が生じると、政府機関や犯罪組織によって密かに悪用されるのではなく、できれば公表されます。

互換性とブラウザ戦争

ウェブの初期段階では、Mosaicと呼ばれるブラウザが市場を支配していました。数年後、勢力図はNetscapeに傾き、今度は代わりにMicrosoftのInternet Explorerに取って代わられました。単一のブラウザが優位になってきた時点で、そのブラウザのベンダーは、ウェブ向けの新機能を一方的に開発する権利があると感じていました。ほとんどのユーザーが最も一般的なブラウザを使用していたため、ウェブサイトはその機能を使用するだけで、他のブラウザは考慮しませんでした。

これが互換性の暗黒時代で、通常「ブラウザ戦争」と呼ばれていました。ウェブ開発者は統一された1つのウェブではなく、2つまたは3つの互換性のないプラットフォームを抱えることになりました。さらに悪いことに、2003年頃に使用されていたブラウザはすべてバグだらけで、もちろん各ブラウザのバグは異なっていました。ウェブページを書く人々にとっては苦難の時代でした。

Netscapeから派生した非営利団体のMozilla Firefoxは、2000年代後半にInternet Explorerの地位に挑戦しました。当時はMicrosoftが競争力を維持することにあまり関心がなかったため、Firefoxはその市場シェアを大きく奪いました。ほぼ同じ時期に、GoogleはChromeブラウザを導入し、AppleのSafariブラウザが人気を集め、1つではなく4つの主要なプレーヤーが存在する状況が生まれました。

新しいプレーヤーは標準とエンジニアリングの手法にもっと真剣に取り組み、互換性の低さとバグを軽減しました。Microsoftは市場シェアが低下したのを見て方向転換し、Internet Explorerに代わるEdgeブラウザでこれらの手法を採用しました。今日ウェブ開発の習得を始めるのであれば、運がいいと思ってください。主要ブラウザの最新バージョンはどれも非常に均一で、バグも比較的小さくなっています。

残念ながら、Firefoxの市場シェアはさらに小さくなり、Edgeは2018年にChromeの中核を包み込むようになったため、この均一性は再び単一のベンダー(今回はGoogle)がブラウザ市場を十分にコントロールし、ウェブがどうあるべきかのアイデアを世界の他の地域に押し付ける形を取る可能性があります。

価値があるのは、この長い一連の歴史的出来事とアクシデントにより、現在のウェブプラットフォームが生まれたことです。次の章では、それに向けてプログラムを作成していきます。