付録

プロトタイプの継承

nullundefined を除き、各プリミティブ データ型にはプロトタイプがあります。これは、値を操作するメソッドを提供する、対応するオブジェクト ラッパーです。プリミティブに対してメソッドまたはプロパティのルックアップが呼び出されると、JavaScript はバックグラウンドでプリミティブをラップし、代わりにラッパー オブジェクトに対してメソッドを呼び出すか、プロパティ検索を実行します。

たとえば、文字列リテラルには独自のメソッドはありませんが、対応する String オブジェクト ラッパーによって .toUpperCase() メソッドを呼び出すことができます。

"this is a string literal".toUpperCase();
> THIS IS A STRING LITERAL

これはプロトタイプ継承と呼ばれ、値に対応するコンストラクタからプロパティとメソッドを継承します。

Number.prototype
> Number { 0 }
>  constructor: function Number()
>  toExponential: function toExponential()
>  toFixed: function toFixed()
>  toLocaleString: function toLocaleString()
>  toPrecision: function toPrecision()
>  toString: function toString()
>  valueOf: function valueOf()
>  <prototype>: Object { … }

値で定義するだけでなく、これらのコンストラクタを使用してプリミティブを作成することもできます。たとえば、String コンストラクタを使用すると、文字列リテラルではなく、文字列オブジェクトが作成されます。このオブジェクトには、文字列値だけでなく、コンストラクタに継承されるすべてのプロパティとメソッドが含まれます。

const myString = new String( "I'm a string." );

myString;
> String { "I'm a string." }

typeof myString;
> "object"

myString.valueOf();
> "I'm a string."

ほとんどの場合、結果として得られるオブジェクトは、定義に使用した値と同様に動作します。たとえば、new Number コンストラクタを使用して数値を定義すると、Number プロトタイプのすべてのメソッドとプロパティを含むオブジェクトが生成されますが、これらのオブジェクトに対しても、数値リテラルの場合と同様に算術演算子を使用できます。

const numberOne = new Number(1);
const numberTwo = new Number(2);

numberOne;
> Number { 1 }

typeof numberOne;
> "object"

numberTwo;
> Number { 2 }

typeof numberTwo;
> "object"

numberOne + numberTwo;
> 3

JavaScript の組み込みのプロトタイパー継承により、実用的メリットはないため、これらのコンストラクタを使用する必要はほとんどありません。コンストラクタを使用してプリミティブを作成すると、結果が単純なリテラルではなくオブジェクトになるため、予期しない結果が生じる可能性があります。

let stringLiteral = "String literal."

typeof stringLiteral;
> "string"

let stringObject = new String( "String object." );

stringObject
> "object"

このため、厳密な比較演算子の使用が複雑になる可能性があります。

const myStringLiteral = "My string";
const myStringObject = new String( "My string" );

myStringLiteral === "My string";
> true

myStringObject === "My string";
> false

自動セミコロン挿入(ASI)

スクリプトの解析中、JavaScript インタープリタは自動セミコロン挿入(ASI)と呼ばれる機能を使用して、省略されたセミコロンのインスタンスを修正できます。JavaScript パーサーは、許可されていないトークンを検出した場合、以下の条件の 1 つ以上が満たされている限り、潜在的な構文エラーを修正するために、そのトークンの前にセミコロンを追加しようとします。

  • このトークンは、前のトークンとは改行で区切られています。
  • このトークンは } です。
  • 前のトークンは ) で、挿入されるセミコロンは、do...while ステートメントの末尾のセミコロンです。

詳しくは、ASI ルールをご覧ください。

たとえば、次のステートメントでセミコロンを省略しても、ASI による構文エラーは発生しません。

const myVariable = 2
myVariable + 3
> 5

ただし、ASI では、同じ行の複数のステートメントを考慮することはできません。同じ行に複数の文を記述する場合は、必ずセミコロンで区切ってください。

const myVariable = 2 myVariable + 3
> Uncaught SyntaxError: unexpected token: identifier

const myVariable = 2; myVariable + 3;
> 5

ASI はエラー修正の試みであり、JavaScript に組み込まれた構文の柔軟性ではありません。セミコロンに基づいて正しいコードが生成されないように、必要に応じてセミコロンを使用します。

厳格モード

JavaScript の記述方法を決定する標準は、この言語の初期設計時に考慮されていた対象をはるかに超える進化を遂げています。JavaScript の想定される動作に変更を加えるたびに、古いウェブサイトでエラーが発生しないようにする必要があります。

ES5 では、スクリプト全体または個々の関数に対してより制限の厳しい言語ルールセットをオプトインできる「厳格モード」を導入することで、既存の実装を損なうことなく JavaScript セマンティクスに関する長年の問題に対処しています。厳格モードを有効にするには、スクリプトまたは関数の最初の行に文字列リテラル "use strict" の後にセミコロンを続けます。

"use strict";
function myFunction() {
  "use strict";
}

厳格モードでは、特定の「安全でない」アクションや非推奨の機能を防止し、一般的な「サイレント」エラーの代わりに明示的なエラーをスローし、将来の言語機能と競合する可能性のある構文の使用を禁止します。たとえば、変数スコープに関する初期の設計上の決定では、デベロッパーが var キーワードを省略することで、含まれているコンテキストに関係なく、変数を宣言する際にグローバル スコープを誤って「汚染」する可能性が高くなります。

(function() {
  mySloppyGlobal = true;
}());

mySloppyGlobal;
> true

最新の JavaScript ランタイムでは、誤または意図的を問わず、この動作に依存しているウェブサイトが機能しなくなるリスクがなければ、この動作を修正できません。代わりに、最新の JavaScript は、デベロッパーが新しい作業で厳格モードにオプトインできるようにし、従来の実装を壊さない新しい言語機能のコンテキストでのみ、厳格モードをデフォルトで有効にするようにすることで、この問題を防ぎます。

(function() {
    "use strict";
    mySloppyGlobal = true;
}());
> Uncaught ReferenceError: assignment to undeclared variable mySloppyGlobal

"use strict" は、文字列リテラルとして記述する必要があります。テンプレート リテラルuse strict)は機能しません。また、意図されたコンテキストで実行可能コードの前に "use strict" を含める必要があります。それ以外の場合、インタープリタはそれを無視します。

(function() {
    "use strict";
    let myVariable = "String.";
    console.log( myVariable );
    sloppyGlobal = true;
}());
> "String."
> Uncaught ReferenceError: assignment to undeclared variable sloppyGlobal

(function() {
    let myVariable = "String.";
    "use strict";
    console.log( myVariable );
    sloppyGlobal = true;
}());
> "String." // Because there was code prior to "use strict", this variable still pollutes the global scope

参照、値

オブジェクトのプロパティ、関数パラメータ配列setmap 内の要素などの変数には、プリミティブ値または参照値のいずれかを含めることができます。

ある変数から別の変数にプリミティブ値が割り当てられると、JavaScript エンジンはその値のコピーを作成して変数に代入します。

オブジェクト(クラス インスタンス、配列、関数)を変数に割り当てると、そのオブジェクトの新しいコピーを作成するのではなく、そのオブジェクトのメモリ内に保存された位置への参照が変数に格納されます。このため、変数によって参照されるオブジェクトを変更すると、その変数に含まれる値だけでなく、参照されるオブジェクトも変更されます。たとえば、オブジェクト参照を含む変数で新しい変数を初期化し、新しい変数を使用してそのオブジェクトにプロパティを追加すると、プロパティとその値が元のオブジェクトに追加されます。

const myObject = {};
const myObjectReference = myObject;

myObjectReference.myProperty = true;

myObject;
> Object { myProperty: true }

これは、オブジェクトを変更する場合だけでなく、厳密な比較を実行する場合にも重要です。オブジェクト間の厳密な等価性を得るには、両方の変数が true と評価するために同じオブジェクトを参照する必要があるためです。構造的に同一であっても、異なるオブジェクトを参照することはできません。

const myObject = {};
const myReferencedObject = myObject;
const myNewObject = {};

myObject === myNewObject;
> false

myObject === myReferencedObject;
> true

メモリ割り当て

JavaScript では自動メモリ管理が使用されます。つまり、開発中にメモリを明示的に割り当てまたは割り当て解除する必要はありません。JavaScript エンジンによるメモリ管理のアプローチの詳細はこのモジュールの対象外ですが、メモリがどのように割り当てられるかを理解することで、参照値の処理に役立つコンテキストを得ることができます。

メモリには「スタック」と「ヒープ」の 2 つの「領域」があります。スタックには、静的データ(プリミティブ値とオブジェクトへの参照)が保存されます。これは、スクリプトの実行前に、このデータを格納するために必要な固定量のスペースを割り当てることができるためです。ヒープにはオブジェクトが格納されますが、実行中にサイズが変わる可能性があるため、動的に割り当てたスペースが必要になります。メモリは、参照のないオブジェクトをメモリから削除する「ガベージ コレクション」と呼ばれるプロセスによって解放されます。

メインスレッド

JavaScript は基本的にシングルスレッド言語であり、「同期」実行モデルでは一度に 1 つのタスクしか実行できません。この順次実行コンテキストはメインスレッドと呼ばれます。

メインスレッドは、HTML の解析、ページ部分のレンダリングと再レンダリング、CSS アニメーションの実行、単純な(テキストのハイライト表示など)から複雑な操作(フォーム要素の操作など)まで、さまざまなユーザー操作の処理など、他のブラウザタスクによって共有されます。ブラウザベンダーは、メインスレッドで実行されるタスクを最適化する方法を見出していますが、複雑なスクリプトでは、依然としてメインスレッドのリソースを過剰に使用し、ページの全体的なパフォーマンスに影響する可能性があります。

一部のタスクは、ウェブワーカーと呼ばれるバックグラウンド スレッドで実行できます。ただし、次のような制限があります。

  • ワーカー スレッドはスタンドアロンの JavaScript ファイルに対してのみ動作できます。
  • ブラウザ・ウィンドウや UI へのアクセスが大幅に減少するか、アクセスできなくなっています。
  • メインスレッドとの通信方法には制限があります。

このような制限があるため、メインスレッドを占有するような、リソースを大量に消費する集中的なタスクに適しています。

コールスタック

「実行コンテキスト」(アクティブに実行されるコード)を管理するために使用されるデータ構造は、コールスタック(単に「スタック」とも呼ばれます)と呼ばれるリストです。スクリプトが最初に実行されると、JavaScript インタープリタは「グローバル実行コンテキスト」を作成してコールスタックにプッシュし、そのグローバル コンテキスト内のステートメントを上から下に 1 つずつ実行します。インタープリタは、グローバル コンテキストの実行中に関数呼び出しを検出すると、その呼び出しの「関数実行コンテキスト」をスタックの一番上にプッシュし、グローバル実行コンテキストを一時停止して、関数実行コンテキストを実行します。

関数が呼び出されるたびに、その呼び出しの関数実行コンテキストがスタックの一番上、現在の実行コンテキストのすぐ上にプッシュされます。コールスタックは「先入れ先出し」方式で動作します。つまり、スタック内で最も新しい関数呼び出しが実行され、解決されるまで続行されます。関数が完了すると、インタープリタは呼び出しスタックからその関数を削除し、その関数呼び出しを含む実行コンテキストが再びスタック内の最上位のアイテムになり、実行を再開します。

これらの実行コンテキストは、その実行に必要な値をキャプチャします。また、親コンテキストに基づいて関数のスコープ内で使用可能な変数と関数を確立し、関数のコンテキストで this キーワードの値を決定して設定します。

イベントループとコールバック キュー

この順次実行とは、コールバック関数を含む非同期タスク(サーバーからのデータの取得、ユーザー操作への応答、setTimeout または setInterval で設定されたタイマーの待機など)が、そのタスクが完了するまでメインスレッドをブロックするか、コールバック関数の実行コンテキストがスタックに追加された瞬間に現在の実行コンテキストを予期せず中断することを意味します。これに対処するために、JavaScript は「イベントループ」と「コールバック キュー」(「メッセージ キュー」と呼ばれることもあります)で構成されるイベント ドリブンの「同時実行モデル」を使用して非同期タスクを管理します。

非同期タスクがメインスレッドで実行されると、コールバック関数の実行コンテキストは、コールスタックの最上位ではなく、コールバック キューに配置されます。イベントループは、「reactor」とも呼ばれるパターンで、コールスタックとコールバック キューのステータスを継続的にポーリングします。コールバック キューにタスクがあり、イベントループがコールスタックが空であると判断した場合、コールバック キューのタスクは 1 つずつスタックに push され、実行されます。