クロージャ - JavaScript

クロージャ
=====

**クロージャ**は、組み合わされた（囲まれた）関数と、その周囲の状態（**レキシカル環境**）への参照の組み合わせです。言い換えれば、クロージャは内側の関数から外側の関数スコープへのアクセスを提供します。JavaScript では、関数が作成されるたびにクロージャが作成されます。

レキシカルスコープ
---------

次のような関数を考えてみてください。

```js
function init() {
  var name = "Mozilla"; // name は、init が作成するローカル変数
  function displayName() {
    // displayName() は内部に閉じた関数
    console.log(name); // 親関数で宣言された変数を使用
  }
  displayName();
}
init();
```

`init()` 関数はローカル変数 `name` を作成し、それから関数 `displayName()` を定義しています。`displayName()` は `init()` の中で定義されている内部関数で、その関数本体の内部でしか利用できません。`displayName()` 自体はローカル変数を持っていませんが、外側の関数で宣言された変数にアクセスできるので、`displayName()` では親関数 `init()` で宣言された変数 `name` を使用できます。しかし、 `displayName()` に同じローカル変数があればそれが使われます。

この JSFiddle リンクを使用してコードを実行すると、`displayName()` 関数内の `console.log()` 文が、その親関数で宣言されている `name` 変数の値を正常に表示していることに注意してください。これはレキシカルスコープの例で、関数が入れ子になっているときにパーサーがどのように変数名を解決するかを記述したものです。レキシカルという言葉は、レキシカルスコープがソースコード内で変数が宣言された場所を使用して、その変数が利用できる場所を決定するという事実を示しています。入れ子の関数は、その外側のスコープで宣言された変数にアクセスすることができます。

この具体例では、変数が宣言された関数本体の中でしかアクセスできないため、スコープは関数スコープと呼ばれます。

### let と const のスコープ

従来（ES6 以前）、JavaScript には関数スコープとグローバルスコープの 2 種類のスコープしかありませんでした。`var` で宣言された変数は、関数内で宣言されたか、関数外で宣言されたかによって、関数スコープかグローバルスコープかのどちらかになります。中括弧で囲まれたブロックはスコープを作成しないので、これはやっかいなことです。

```js
if (Math.random() > 0.5) {
  var x = 1;
} else {
  var x = 2;
}
console.log(x);
```

ブロックがスコープを作成する他の言語（例えば C や Java）から来た人にとって、上記のコードは、`console.log` の行がどちらのブロックでも `x` のスコープ外になるため、エラーが発生するはずです。しかし、ブロックは `var` のスコープを作成しないので、ここでの `var` の記述は実際にはグローバル変数を作成することになります。また、クロージャと組み合わせると実際にバグが発生することを示す実例が下記で紹介されています。

ES6 で JavaScript は `let` と `const` 宣言を導入し、他にも一時的なデッドゾーン など、ブロックスコープ変数を作成できるようにしました。

```js
if (Math.random() > 0.5) {
  const x = 1;
} else {
  const x = 2;
}
console.log(x); // ReferenceError: x is not defined
```

要するに、ブロックは ES6 でようやくスコープとして扱われるようになりましたが、`let` や `const` で変数を宣言した場合に限ります。さらに、ES6 ではモジュールが導入され、別の種類のスコープが導入されました。クロージャはこれらすべてのスコープで変数を捕捉することができるのですが、これについては後ほど紹介します。

クロージャ
-----

今度は次のような例を考えます。

```js
function makeFunc() {
  const name = "Mozilla";
  function displayName() {
    console.log(name);
  }
  return displayName;
}

const myFunc = makeFunc();
myFunc();
```

このコードを実行すると、前回例に挙げた `init()` 関数と全く同じ効果があります。異なる形で興味深いのは、`displayName()` の内部関数が、実行される前に外部関数から返されることです。

このコードが動作するということは直感的に理解できないかもしれません。いくつかのプログラミング言語では、関数内部のローカル変数はその関数が実行されている間だけ存在します。一旦 `makeFunc()` の実行が完了したら、name 変数はもう必要とされなくなると考えた方が筋は通っています。ただこのコードが期待したとおりに動くという事は、これは明らかに JavaScript にはあてはまりません。

この理由は、JavaScript の関数はクロージャとなるためです。クロージャは関数とその関数が作られた環境という 2 つのものの組み合わせです。この環境は、クロージャが作られた時点でスコープ内部にあったあらゆる変数によって構成されています。この場合、`myFunc` は `makeFunc` が実行された時に作られた `displayName` 関数のインスタンスへの参照です。`displayName` のインスタンスはレキシカル環境への参照を保持し、そこに `name` 変数が存在します。このため、`makeFunc` が実行された時に、`name` 変数が残っていて "Mozilla" が `console.log` に渡されます。

ここにもう少し面白い例があります。`makeAdder` 関数です。

```js
function makeAdder(x) {
  return function (y) {
    return x + y;
  };
}

const add5 = makeAdder(5);
const add10 = makeAdder(10);

console.log(add5(2)); // 7
console.log(add10(2)); // 12
```

この例では、1 つの引数 `x` を取り、新しい関数を返す `makeAdder(x)` 関数を定義しています。返される関数は 1 つの引数 `y` を取り、`x` と `y` の和を返します。

要するに、`makeAdder` は関数ファクトリーです。これは与えられた引数に特定の値を足す関数を作ります。上の例では関数ファクトリーを使って 2 つの新しい関数を作成しています。1 つは引数に 5 を加えるもので、もう 1 つは 10 を加えるものです。

`add5` と `add10` は両方ともクロージャです。両者は同じ関数本体の定義を共有していますが、保有している環境は異なります。`add5` の環境では `x` は 5 で、`add10` の環境では `x` は 10 です。

実用的なクロージャ
---------

クロージャが有益なのは、データ（レキシカル環境）とそのデータを使用する関数を関連付けることができるからです。これは、オブジェクト指向プログラミングと明らかに並列しています。オブジェクトでは、データ（オブジェクトのプロパティ）を 1 つまたは複数のメソッドに関連付けることができます。

したがって、メソッドを 1 つだけ持つオブジェクトを使いたくなるような状況ならば、どんな時でもクロージャを使う事ができます。

ウェブではこのような状況はよくあります。私たちが書く JavaScript のコードは大半がイベントベースです。つまり、ある動作を定義し、それを click や keypress といったユーザーによって引き起こされるイベントに取り付けます。私たちのコードの多くはコールバック、すなわちイベントに反応して実行される単独の関数として取り付けられます。

実例を挙げましょう。あるページにそのページのテキストの大きさを調整するためのボタンを追加しようとしているとします。1 つの方法として、まず `body` 要素の font-size をピクセル数で指定して、そのページ内の (見出しなどの) 他の要素のサイズを相対単位 `em` で設定します。

```css
body {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 12px;
}

h1 {
  font-size: 1.5em;
}

h2 {
  font-size: 1.2em;
}
```

これから作る対話式のテキストサイズ調整ボタンは、`body` 要素の `font-size` プロパティを変更し、その変更は相対単位によってページ上のほかの要素にも適用されます。

JavaScript のコード:

```js
function makeSizer(size) {
  return function () {
    document.body.style.fontSize = `${size}px`;
  };
}

const size12 = makeSizer(12);
const size14 = makeSizer(14);
const size16 = makeSizer(16);
```

`size12`、`size14`、`size16` はそれぞれ body のテキストサイズを 12、14、16 px に変更する関数になっています。これらは以下のようにしてボタン (この場合はリンク) に取り付けられます。

```js
document.getElementById("size-12").onclick = size12;
document.getElementById("size-14").onclick = size14;
document.getElementById("size-16").onclick = size16;
```

```html
<button id="size-12">12</button>
<button id="size-14">14</button>
<button id="size-16">16</button>
```

JSFiddle でコードを実行します。

クロージャでプライベートメソッドを模倣する
---------------------

Java などの言語ではプライベートなメソッドを宣言することが出来ます。これは同じクラス内にあるほかのメソッドからのみ呼び出せるメソッドのことです。

JavaScript には、クラスが登場するまで、プライベートメソッドを宣言するネイティブの方法はありませんでしたが、クロージャを使うとプライベートメソッドを模倣することができます。プライベートメソッドはコードへのアクセスを制限するのに役立つだけではなく、コードのパブリックインターフェイスが不要なメソッドでいっぱいになるのを防ぐため、グローバル名前空間を管理するのに非常に有効です。

モジュールパターンとしても知られるクロージャを使って、プライベートな関数と変数にアクセスできるパブリック関数を定義するにはこのようにします。

```js
const counter = (function () {
  let privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }

  return {
    increment() {
      changeBy(1);
    },

    decrement() {
      changeBy(-1);
    },

    value() {
      return privateCounter;
    },
  };
})();

console.log(counter.value()); // 0.

counter.increment();
counter.increment();
console.log(counter.value()); // 2.

counter.decrement();
console.log(counter.value()); // 1.
```

ここでは色々なことが行われています。前の例ではクロージャがそれぞれ独自の環境を持っていましたが、この例では環境が 1 つだけ作成され、その環境は `counter.increment`, `counter.decrement`, `counter.value` という 3 つの関数によって共有されています。

この共有レキシカル環境は、*定義されるとすぐに実行される*（IIFE とも呼ばれます）無名関数の本文で作成されています。この環境は変数 `privateCounter` と関数 `changeBy` という 2 つのプライベートアイテムを含んでいます。これらはどちらも無名関数の外側からは直接アクセス出来ません。その代わり、この無名ラッパー関数から返される 3 つのパブリック関数からはアクセスできます。

これら 3 つのパブリック関数は同じ環境を共有するクロージャです。JavaScript のレキシカルスコープにより、これらの関数はそれぞれが変数 `privateCounter` と関数 `changeBy` にアクセスできます。

```js
const makeCounter = function () {
  let privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment() {
      changeBy(1);
    },

    decrement() {
      changeBy(-1);
    },

    value() {
      return privateCounter;
    },
  };
};

const counter1 = makeCounter();
const counter2 = makeCounter();

console.log(counter1.value()); // 0.

counter1.increment();
counter1.increment();
console.log(counter1.value()); // 2.

counter1.decrement();
console.log(counter1.value()); // 1.
console.log(counter2.value()); // 0.
```

2 つのカウンターが互いに独立した状態を維持していることに注目してください。各クロージャは、独自のクロージャを介して異なるバージョンの `privateCounter` 変数を参照しています。カウンターのいずれかが呼び出されるたびに、この変数の値を変更することで、そのレキシカル環境が変化します。あるクロージャで変数値を変更しても、もう一方のクロージャの値には影響しません。

**メモ:** このようにしてクロージャを使うと、普通はオブジェクト指向プログラミングに付き物のいくつかの利点、具体的には*データの隠蔽*や*カプセル化*が利用できるようになります。

クロージャのスコープチェーン
--------------

すべてのクロージャには 3 つのスコープがあります。

* ローカルスコープ（独自のスコープ）
* 外側の関数スコープ
* グローバルスコープ

よくある間違いは、外側の関数がネストされた関数である場合、外側の関数スコープへのアクセスにはさらに外側の関数スコープが含まれており。実質的にスコープチェーンが生じていることに気づかないことです。次のコードの例を考えてみましょう。

```js
// グローバルスコープ
const e = 10;
function sum(a) {
  return function (b) {
    return function (c) {
      // 外側の関数スコープ
      return function (d) {
        // ローカルスコープ
        return a + b + c + d + e;
      };
    };
  };
}

console.log(sum(1)(2)(3)(4)); // 20
```

無名関数を使わずに書くこともできます。

```js
// グローバルスコープ
const e = 10;
function sum(a) {
  return function sum2(b) {
    return function sum3(c) {
      // 外側の関数スコープ
      return function sum4(d) {
        // ローカルスコープ
        return a + b + c + d + e;
      };
    };
  };
}

const sum2 = sum(1);
const sum3 = sum2(2);
const sum4 = sum3(3);
const result = sum4(4);
console.log(result); // 20
```

上記の例では、一連のネストされた関数があり、それらはすべて外側の関数スコープにアクセスできます。この文脈では、クロージャは*すべて*の外側の関数スコープにアクセスできると言えます。

クロージャはブロックスコープやモジュールスコープの変数も取り込むことができます。例えば、以下はブロックスコープにある変数 `y` に対してクロージャを作成します。

```js
function outer() {
  const x = 5;
  if (Math.random() > 0.5) {
    const y = 6;
    return () => console.log(x, y);
  }
}

outer()(); // Logs 5 6
```

モジュール上のクロージャは、もっと興味深いものになります。

```js
// myModule.js
let x = 5;
export const getX = () => x;
export const setX = (val) => {
  x = val;
};
```

ここでは、モジュールはモジュールスコープの変数 `x` を隠蔽するゲッターセッター関数のペアをエクスポートします。他のモジュールから `x` に直接アクセスできない場合でも、この関数で読み書きができます。

```js
import { getX, setX } from "./myModule.js";

console.log(getX()); // 5
setX(6);
console.log(getX()); // 6
```

クロージャはインポートされた値も隠蔽することができ、元の値が変わるとインポートされた値もそれに応じて変わるため、*ライブバインド* と見なされます。

```js
// myModule.js
export let x = 1;
export const setX = (val) => {
  x = val;
};
```

```js
// closureCreator.js
import { x } from "./myModule.js";

export const getX = () => x; // インポートされたライブバインドを隠蔽
```

```js
import { getX } from "./closureCreator.js";
import { setX } from "./myModule.js";

console.log(getX()); // 1
setX(2);
console.log(getX()); // 2
```

ループ内でクロージャを作成: 良くある間違い
----------------------

`let` キーワードが導入される前までは、ループの内部でクロージャが作成された時にある問題がよく起こっていました。次のような例を考えてみます。

```html
<p id="help">ここにヘルプが表示されます</p>
<p>E メール: <input type="text" id="email" name="email" /></p>
<p>名前: <input type="text" id="name" name="name" /></p>
<p>年齢: <input type="text" id="age" name="age" /></p>
```

```js
function showHelp(help) {
  document.getElementById("help").textContent = help;
}

function setupHelp() {
  var helpText = [
    { id: "email", help: "あなたの E メールアドレス" },
    { id: "name", help: "あなたのフルネーム" },
    { id: "age", help: "あなたの年齢 (17 歳以上)" },
  ];

  for (var i = 0; i < helpText.length; i++) {
    // この行で `var` を使用していることが原因
    var item = helpText[i];
    document.getElementById(item.id).onfocus = function () {
      showHelp(item.help);
    };
  }
}

setupHelp();
```

JSFiddle でコードを実行してみてください。

配列 `helpText` は 3 つのヘルプを定義しており、それぞれが文書内の入力フィールドの ID と関連付けられています。ループがこれらの定義を巡回して、それぞれの入力フィールドの `onfocus` イベントをそれに関連付けられたヘルプを表示するメソッドと結び付けています。

このコードを実行してみると、期待したとおりには動かないのが分かります。どのフィールドにフォーカスしても、表示されるのは年齢についてのメッセージです。

こうなる理由は、`onfocus` に代入された関数がクロージャだからです。このクロージャは、関数定義と、`setupHelp` 関数スコープから捕捉された環境から成っています。クロージャは 3 つ作られましたが、これらはみな 1 つの同じ環境を共有しています。`onfocus` コールバックが実行される時にはループはすべて終了しており、変数 `item`（3 つのクロージャ全てに共有されている）は `helpText` リストの最後の項目を示したままにされています。

こういった場合の解決策の 1 つとして、より多くのクロージャを使う方法があります。具体的には、前に述べたような関数ファクトリーを使います。

```js
function showHelp(help) {
  document.getElementById("help").textContent = help;
}

function makeHelpCallback(help) {
  return function () {
    showHelp(help);
  };
}

function setupHelp() {
  var helpText = [
    { id: "email", help: "あなたの E メールアドレス" },
    { id: "name", help: "あなたのフルネーム" },
    { id: "age", help: "あなたの年齢 (17 歳以上)" },
  ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = makeHelpCallback(item.help);
  }
}

setupHelp();
```

JSFiddle でコードを実行してみてください。

これは期待通り動きます。全てのコールバックが 1 つの環境を共有するのではなく、`makeHelpCallback` 関数がそれぞれに対して*新しいレキシカル環境*を作っており、そこでは `help` が配列 `helpText` の対応する文字列を参照しています。

上のものを無名クロージャを使って書く、他の方法もあります。

```js
function showHelp(help) {
  document.getElementById("help").innerHTML = help;
}

function setupHelp() {
  var helpText = [
    { id: "email", help: "あなたの E メールアドレス" },
    { id: "name", help: "あなたのフルネーム" },
    { id: "age", help: "あなたの年齢 (17 歳以上)" },
  ];

  for (var i = 0; i < helpText.length; i++) {
    (function () {
      var item = helpText[i];
      document.getElementById(item.id).onfocus = function () {
        showHelp(item.help);
      };
    })(); // item の現在値（反復処理まで保存される）を持つ即時イベントリスナーの装着
  }
}

setupHelp();
```

これ以上クロージャを使いたくない場合、`let` や `const` キーワードも使用できます。

```js
function showHelp(help) {
  document.getElementById("help").textContent = help;
}

function setupHelp() {
  const helpText = [
    { id: "email", help: "あなたの E メールアドレス" },
    { id: "name", help: "あなたのフルネーム" },
    { id: "age", help: "あなたの年齢 (17 歳以上)" },
  ];

  for (let i = 0; i < helpText.length; i++) {
    const item = helpText[i];
    document.getElementById(item.id).onfocus = () => {
      showHelp(item.help);
    };
  }
}

setupHelp();
```

この例では `var` の代わりに `const` を使っているため、すべてのクロージャがブロックスコープの変数をバインドしており、つまり追加のクロージャは要求されません。

他の方法として、以下のように `forEach()` を使用して配列 `helpText` を操作し、それぞれの `<input>` にリスナーを割り当てることができます。

```js
function showHelp(help) {
  document.getElementById("help").textContent = help;
}

function setupHelp() {
  var helpText = [
    { id: "email", help: "あなたの E メールアドレス" },
    { id: "name", help: "あなたのフルネーム" },
    { id: "age", help: "あなたの年齢 (17 歳以上)" },
  ];

  helpText.forEach(function (text) {
    document.getElementById(text.id).onfocus = function () {
      showHelp(text.help);
    };
  });
}

setupHelp();
```

パフォーマンスへの配慮
-----------

前回述べたように、それぞれの関数インスタンスは、独自のスコープとクロージャを管理します。したがって、クロージャが具体的なタスクに必要でない場合に、不必要に他の関数の中に関数を作成することは、処理速度とメモリー消費の両面でスクリプトのパフォーマンスに悪影響を与えるので、賢明ではありません。

例えば、新しくオブジェクト/クラスを作成する時、一般的にメソッドはオブジェクトのコンストラクターの中で定義するのではなく、オブジェクトのプロトタイプに結びつけるべきです。コンストラクタの中で定義してしまうと、コンストラクターが呼び出されるたびに（つまりオブジェクトが作成されるたびに）メソッドが再代入されてしまうことになるからです。

以下のケースを考えてみてください。

```js
function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
  this.getName = function () {
    return this.name;
  };

  this.getMessage = function () {
    return this.message;
  };
}
```

上のコードは、この具体的な例ではクロージャを使用することの恩恵を受けられないので、代わりに以下のようにクロージャを使用しないように書き換えることができます。

```js
function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
MyObject.prototype = {
  getName() {
    return this.name;
  },
  getMessage() {
    return this.message;
  },
};
```

ただし、プロトタイプの再定義は推奨されません。以下の例のように既存のプロトタイプに追加するのがより好ましい方法です。

```js
function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
MyObject.prototype.getName = function () {
  return this.name;
};
MyObject.prototype.getMessage = function () {
  return this.message;
};
```

上の 2 つの例では、プロトタイプが継承されて全てのオブジェクトによって共有されるため、オブジェクトが作成されるたびにメソッドが定義されずに済みます。詳しくは継承とプロトタイプチェーンを参照して下さい。


プロパティの列挙可能性と所有権 - JavaScript

プロパティの列挙可能性と所有権
===============

列挙可能プロパティは、内部の列挙可能（enumerable）フラグが true に設定されているプロパティです。これは、単純な代入や初期化で作成されたプロパティのデフォルトです (Object.defineProperty で追加したプロパティはデフォルトで列挙可能性が false になります）。プロパティのキーが Symbol でない限り、列挙可能なプロパティは for...in ループにの対象になります。プロパティの所有権は、プロパティがプロトタイプチェーンではなく、オブジェクトに直接属しているかどうかによって決まります。オブジェクトのプロパティはまとめて取り扱うこともでき、プロパティを検出、反復、列挙、取得するための多くの組み込み機能があります。以下に、使用可能なチャートと不足しているカテゴリを取得する方法を示すサンプルコードを示します。

プロパティの列挙可能性と所有権の検出、取得、反復の組み込みメソッド| 機能 | 所有するオブジェクト | 所有するオブジェクトとプロトタイプチェーン | プロトタイプチェーンのみ |
| --- | --- | --- | --- |
| 検出 |

| 列挙可能 | 列挙不可能 | 列挙可能と列挙不可能 |
| --- | --- | --- |
| `propertyIsEnumerable`
`hasOwnProperty` |
`hasOwnProperty`
 –
 `propertyIsEnumerable`
 を使用して列挙可能なものを除外するようにフィルターリング
  | `hasOwnProperty` |

 |

| 列挙可能 | 列挙不可能 | 列挙可能と列挙不可能 |
| --- | --- | --- |
| 追加のコードが必要 | 追加のコードが必要 | `in` |

 | 追加のコードが必要 |
| 取得 |

| 列挙可能 | 列挙不可能 | 列挙可能と列挙不可能 |
| --- | --- | --- |
| `Object.keys`
`getOwnPropertyNames`
`getOwnPropertySymbols` | `getOwnPropertyNames`,
 `getOwnPropertySymbols`
 –
 `propertyIsEnumerable`
 を使用して列挙可能なものを除外するようにフィルターリング
  | `getOwnPropertyNames`
`getOwnPropertySymbols` |

 | 追加のコードが必要 | 追加のコードが必要 |
| 反復 |

| 列挙可能 | 列挙不可能 | 列挙可能と列挙不可能 |
| --- | --- | --- |
| `Object.keys`
`getOwnPropertyNames`
`getOwnPropertySymbols` | `getOwnPropertyNames`,
 `getOwnPropertySymbols`
 –
 `propertyIsEnumerable`
 を使用して列挙可能なものを除外するようにフィルターリング
  | `getOwnPropertyNames`
`getOwnPropertySymbols` |

 |

| 列挙可能 | 列挙不可能 | 列挙可能と列挙不可能 |
| --- | --- | --- |
| `for..in`
(symbol を除く) | 追加のコードが必要 | 追加のコードが必要 |

 | 追加のコードが必要 |

列挙可能性/所有権によるプロパティの取得
--------------------

以下に示すのは全てのケースで最も効率的なアルゴリズムではなく、簡潔なデモであることに注意してください。

* 検出は以下の方法で行うことができます。
 `SimplePropertyRetriever.使いたい get メソッド(obj).indexOf(prop) > -1`
* 反復は以下の方法で行うことができます。
 `SimplePropertyRetriever.使いたい get メソッド(obj).forEach(function (value, prop) {});` (または `filter()`, `map()` などを使う)

```js
var SimplePropertyRetriever = {
  getOwnEnumerables: function (obj) {
    return this.\_getPropertyNames(obj, true, false, this._enumerable);
    // Or could use for..in filtered with hasOwnProperty or just this: return Object.keys(obj);
  },
  getOwnNonenumerables: function (obj) {
    return this.\_getPropertyNames(obj, true, false, this._notEnumerable);
  },
  getOwnEnumerablesAndNonenumerables: function (obj) {
    return this.\_getPropertyNames(
      obj,
      true,
      false,
      this._enumerableAndNotEnumerable,
    );
    // Or just use: return Object.getOwnPropertyNames(obj);
  },
  getPrototypeEnumerables: function (obj) {
    return this.\_getPropertyNames(obj, false, true, this._enumerable);
  },
  getPrototypeNonenumerables: function (obj) {
    return this.\_getPropertyNames(obj, false, true, this._notEnumerable);
  },
  getPrototypeEnumerablesAndNonenumerables: function (obj) {
    return this.\_getPropertyNames(
      obj,
      false,
      true,
      this._enumerableAndNotEnumerable,
    );
  },
  getOwnAndPrototypeEnumerables: function (obj) {
    return this.\_getPropertyNames(obj, true, true, this._enumerable);
    // Or could use unfiltered for..in
  },
  getOwnAndPrototypeNonenumerables: function (obj) {
    return this.\_getPropertyNames(obj, true, true, this._notEnumerable);
  },
  getOwnAndPrototypeEnumerablesAndNonenumerables: function (obj) {
    return this.\_getPropertyNames(
      obj,
      true,
      true,
      this._enumerableAndNotEnumerable,
    );
  },
  // Private static property checker callbacks
  \_enumerable: function (obj, prop) {
    return obj.propertyIsEnumerable(prop);
  },
  \_notEnumerable: function (obj, prop) {
    return !obj.propertyIsEnumerable(prop);
  },
  \_enumerableAndNotEnumerable: function (obj, prop) {
    return true;
  },
  // Inspired by http://stackoverflow.com/a/8024294/271577
  \_getPropertyNames: function getAllPropertyNames(
    obj,
 iterateSelfBool,
 iteratePrototypeBool,
 includePropCb,
  ) {
    var props = [];

    do {
      if (iterateSelfBool) {
        Object.getOwnPropertyNames(obj).forEach(function (prop) {
          if (props.indexOf(prop) === -1 && includePropCb(obj, prop)) {
            props.push(prop);
          }
        });
      }
      if (!iteratePrototypeBool) {
        break;
      }
      iterateSelfBool = true;
    } while ((obj = Object.getPrototypeOf(obj)));

    return props;
  },
};
```

検出テーブル
------

|  | `in` | `for..in` | `obj.hasOwnProperty` | `obj.propertyIsEnumerable` | `Object.keys` | `Object.getOwnPropertyNames` | `Object.getOwnPropertyDescriptors` | `Reflect.ownKeys()` |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 列挙可能 | true | true | true | true | true | true | true | true |
| 列挙不可能 | true | false | true | false | false | true | true | true |
| Symbols キー | true | false | true | true | false | false | true | true |
| 継承された列挙可能 | true | true | false | false | false | false | false | false |
| 継承された列挙不可能 | true | false | false | false | false | false | false | false |
| 継承された Symbols キー | true | false | false | false | false | false | false | false |

関連情報
----

* `in`
* `for..in`
* `Object.hasOwnProperty()`
* `Object.propertyIsEnumerable()`
* `Object.getOwnPropertyNames()`
* `Object.keys()`
* `Object.getOwnPropertyDescriptors()`


JavaScript ガイド - JavaScript

JavaScript ガイド
==============

この JavaScript ガイドでは、JavaScript の使い方を紹介し、この言語の概要を説明します。JavaScript の機能についてもっと知りたい場合は、JavaScript リファレンス を参照してください。

目次
--

このガイドは、いくつかの章に分かれています。

* 入門編
このガイドについて
JavaScript について
JavaScript と Java
ECMAScript
ツール
Hello World
* 文法とデータ型
基本構文とコメント
宣言
変数のスコープ
変数の巻き上げ
データ構造とデータ型
リテラル
* 制御フローとエラー処理
`if...else`
`switch`
`try`/`catch`/`throw`
エラーオブジェクト
* ループと反復処理
`for
while
do...while
break/continue
for..in
for..of`

* 関数
関数の定義
関数の呼び出し
関数のスコープ
クロージャ
実引数 と 仮引数
アロー関数
* 式と演算子
代入演算子 と 比較演算子
算術演算子
ビット演算子 と 論理演算子
条件演算子
* 数値と日付 数値リテラル
`Number` オブジェクト
`Math` オブジェクト
`Date` オブジェクト
* テキスト処理
文字列リテラル
`String` オブジェクト
テンプレートリテラル
国際化
正規表現

* インデックス付きコレクション
配列
型付き配列
* キー付きコレクション
`Map`
`WeakMap`
`Set`
`WeakSet`
* オブジェクトを利用する
オブジェクトとそのプロパティ
新しいオブジェクトの作成
メソッドの定義
getter と setter
* オブジェクトモデルの詳細
プロトタイプベース言語
階層の作成
継承

* Promiseを使う
保証
Promise チェーン
エラーの伝搬
合成 (Composition)
タイミング
* イテレータとジェネレータ
イテレータ
反復可能 (イテラブル)
ジェネレータ
* メタプログラミング
`プロキシ`
ハンドラとトラップ
取り消し可能
Proxy
`Reflect`
* JavaScript モジュール
エクスポート
インポート
デフォルトエクスポート
名前を変更する
モジュールの集約
動的なモジュールの読み込み
* 次のページ »


継承とプロトタイプチェーン - JavaScript

継承とプロトタイプチェーン
=============

JavaScript は動的であり、固定的な型がないことから、クラスベースの言語（Java や C++ など）を経験した開発者にとっては少し分かりにくいものです。

継承において、 JavaScript が持っている構成要素はただひとつ、オブジェクトだけです。それぞれのオブジェクトは、その**プロトタイプ**と呼ばれる別のオブジェクトへのリンクを持つプライベートプロパティを持っています。そのプロトタイプオブジェクト自身もプロトタイプを持ち、 `null` をプロトタイプとするオブジェクトに到達するまで続きます。定義上、 `null` はプロトタイプを持たず、この**プロトタイプチェーン**の最後のリンクとして機能します。プロトタイプチェーンのどのメンバーも、実行時に変更したり交換したりすることが可能なので、静的呼び出しという概念は、 JavaScript には存在しません。

この混乱はしばしば JavaScript の弱点のひとつとみなされますが、プロトタイプ継承モデルそのものは、実際には古典的なモデルよりも強力なものです。例えば、プロトタイプモデルの上に古典的なモデルを構築するのはかなり手軽です。クラスはこうやって実装されているのです。

クラスは現在広く採用され、 JavaScript の新しいパラダイムとなっていますが、クラスが新しい継承パターンをもたらすわけではありません。クラスはプロトタイプのメカニズムのほとんどを抽象化してしまいますが、プロトタイプがその下でどのように動作するかを理解することは、今でも有用だと言えます。

プロトタイプチェーンと継承
-------------

### プロパティの継承

JavaScript のオブジェクトはプロパティ（**自身のプロパティ**を指す）の動的な「袋」です。 JavaScript のオブジェクトは、プロトタイプオブジェクトへのリンクを持っています。あるオブジェクトのプロパティにアクセスしようとすると、オブジェクトだけでなく、オブジェクトのプロトタイプ、プロトタイプのプロトタイプへと、一致する名前のプロパティが得られるか、プロトタイプチェーンの終端に到達するまで、プロパティの探索が行われます。

**メモ:** ECMAScript 標準に従い、 `someObject.[[Prototype]]` という表記を `someObject` のプロトタイプを示すのに使用しています。内部スロット `[[Prototype]]` には `Object.getPrototypeOf()` と `Object.setPrototypeOf()` 関数でアクセスすることができます。これは、標準ではないが、多くのJavaScriptエンジンで事実上実装されている JavaScript のアクセサー `__proto__` と同等のものです。混乱を避けつつ簡潔にするために、ここでは `obj.__proto__` の使用を避け、代わりに `obj.[[Prototype]]` を使用することにします。これは `Object.getPrototypeOf(obj)` に対応するものです。

これを関数の `func.prototype` プロパティと混同してはいけません。このプロパティは、指定された関数がコンストラクターとして使用されたときに作成されるオブジェクトのすべての「インスタンス」に割り当てられる `[[Prototype]]` を指定します。コンストラクター関数の `prototype` プロパティについては、後の節で説明します。

オブジェクトの `[[Prototype]]` を指定する方法はいくつかありますが、それは 後の節 に掲載されています。今のところ、説明のために `__proto__` 構文を使用します。ここで注目すべきは、`{ __proto__: ... }` の構文は、 `obj.__proto__` アクセサーとは異なります。前者は標準的なものであり、非推奨ではありません。

`{ a: 1, b: 2, __proto__: c }` のようなオブジェクトリテラルでは、値 `c` （これは `null` か他のオブジェクトでなければなりません）が、リテラル上で `[[Prototype]]` として表されるオブジェクトになるのに対し、 `a` や `b` など他のキーはオブジェクトの *自身のプロパティ* となります。この構文では、 `[[Prototype]]` はオブジェクトの単なる「内部プロパティ」になるため、とても自然に読むことができます。

では、プロパティにアクセスを試みたときに、何が起こるのかを見てみましょう。

```js
const o = {
  a: 1,
  b: 2,
  // \_\_proto\_\_ は [[Prototype]] を設定します。ここでは
  // 他のオブジェクトリテラルとして指定します。
  \_\_proto\_\_: {
    b: 3,
    c: 4,
  },
};

// o.[[Prototype]] は b と c プロパティを持っています。
// o.[[Prototype]].[[Prototype]] は Object.prototype です（意味は後で説明します）。
// そして、 o.[[Prototype]].[[Prototype]].[[Prototype]] は null です。
// null は定義上、 [[Prototype]] を持たないので、これでプロトタイプチェーンは終了です。
// したがって、完全なプロトタイプチェーンは次のようになります。
// { a: 1, b: 2 } ---> { b: 3, c: 4 } ---> Object.prototype ---> null

console.log(o.a); // 1
// 'a' という自身のプロパティが o にあるでしょうか？はい、その値は 1 です。

console.log(o.b); // 2
// 'b' という自身のプロパティが o にあるでしょうか？はい、その値は 2 です。
// プロトタイプにも 'b' プロパティがありますが、アクセスされません。
// これを「プロパティの隠蔽」と呼びます。

console.log(o.c); // 4
// 'c' という自身のプロパティが o にあるでしょうか？いいえ、そのプロトタイプを確認します。
// 'c' という自身のプロパティが o.[[Prototype]] にあるでしょうか？はい、その値は 4 です。

console.log(o.d); // undefined
// 'd' という自身のプロパティが o にあるでしょうか？いいえ、そのプロトタイプを確認します。
// 'd' という自身のプロパティが o.[[Prototype]] にあるでしょうか？いいえ、そのプロトタイプを確認します。
// o.[[Prototype]].[[Prototype]] は Object.prototype であり、既定では 'd' というプロパティはありません。そのプロトタイプを確認します。
// o.[[Prototype]].[[Prototype]].[[Prototype]] は null であるため探索を中止し、
// プロパティが見つからなかったため undefined を返します。
```

あるオブジェクトにプロパティを設定すると、自身のプロパティが作られます。この取得と設定の動作のルールの唯一の例外は、ゲッターまたはセッターが介在するときです。

同様に、より長いプロトタイプチェーンを作成することができ、プロパティはそれらすべてに対して探索されます。

```js
const o = {
  a: 1,
  b: 2,
  // \_\_proto\_\_ は [[Prototype]] を設定します。ここでは
  // 他のオブジェクトリテラルとして指定します。
  \_\_proto\_\_: {
    b: 3,
    c: 4,
    \_\_proto\_\_: {
      d: 5,
    },
  },
};

// { a: 1, b: 2 } ---> { b: 3, c: 4 } ---> { d: 5 } ---> Object.prototype ---> null

console.log(o.d); // 5
```

### 「メソッド」の継承

JavaScript には、クラスベースの言語が定義するような「メソッド」はありません。 JavaScript ではどの関数も、オブジェクトのプロパティという形で追加することができます。継承された関数は、上で見せたようなプロパティの隠蔽（この場合はメソッドのオーバーライドの形）を含め、他のどのプロパティとも同じように動作します。

継承された関数が実行されるときの `this` の値は、その関数を自身のプロパティとして持つプロトタイプオブジェクトではなく、継承したオブジェクトを指します。

```js
const parent = {
  value: 2,
  method() {
    return this.value + 1;
  },
};

console.log(parent.method()); // 3
// この場合に parent.method が呼び出されたとき、 'this' は parent を指します。

// child は parent を継承するオブジェクト
const child = {
  \_\_proto\_\_: parent,
};
console.log(child.method()); // 3
// child.method が呼び出されると、 'this' は child を参照します。
// つまり、parent のメソッドを child が継承すると、child に
// プロパティ 'value' が求まることになります。しかし、child は
// 'value' という自分自身で呼び出されるプロパティを持っていないので、
// プロパティは parent.value である [[Prototype]] で見つかります。

child.value = 4; // child のプロパティ 'value' に値 4 を代入する。
// これは parent の 'value' プロパティを隠蔽するものです。
// child オブジェクトは次のように見えるようになります。
// { value: 4, \_\_proto\_\_: { value: 2, method: [Function] } }
console.log(child.method()); // 5
// child には 'value' プロパティがあるので、 'this.value' は
// child.value になる
```

コンストラクター
--------

プロトタイプの威力は、特にメソッドの場合、すべてのインスタンスに存在する必要がある場合に、プロパティのセットを再利用できることです。例えば、いくつかのボックスを作成し、それぞれのボックスが `getValue` 関数でアクセス可能な値を含むオブジェクトであるとします。素朴な実装は次のようになります。

```js
const boxes = [
  {
    value: 1,
    getValue() {
      return this.value;
    },
  },
  {
    value: 2,
    getValue() {
      return this.value;
    },
  },
  {
    value: 3,
    getValue() {
      return this.value;
    },
  },
];
```

これは、各インスタンスが同じことを行う自分自身で関数プロパティを持つため、冗長で不要なものであり、劣悪です。その代わりに、 `getValue` をすべてのボックスの `[[Prototype]]` に移動させることができます。

```js
const boxPrototype = {
  getValue() {
    return this.value;
  },
};

const boxes = [
  { value: 1, \_\_proto\_\_: boxPrototype },
  { value: 2, \_\_proto\_\_: boxPrototype },
  { value: 3, \_\_proto\_\_: boxPrototype },
];
```

こうすることで、すべてのボックスの `getValue` メソッドが同じ関数を参照するようになり、メモリ使用量を減らすことができます。しかし、オブジェクトを生成するたびに `__proto__` を手動で結びつけるのは、まだとても不便です。そこで、オブジェクトを生成するたびに `[[Prototype]]` を自動的に設定する *コンストラクター* 関数を使用することになります。コンストラクターは `new` を使って呼び出される関数です。

```js
// コンストラクター関数
function Box(value) {
  this.value = value;
}

// Box() コンストラクターで作成されたすべてのボックスには、
// 以下のプロパティがあります。
Box.prototype.getValue = function () {
  return this.value;
};

const boxes = [new Box(1), new Box(2), new Box(3)];
```

`new Box(1)` は `Box` コンストラクター関数から生成された「インスタンス」と言います。 `Box.prototype` は前回作成した `boxPrototype` オブジェクトと大きな違いはなく、ただのオブジェクトです。コンストラクター関数から作成されたインスタンスは、自動的にコンストラクターの `prototype` プロパティを `[[Prototype]]` として保有します。つまり、 `Object.getPrototypeOf(new Box()) === Box.prototype` となります。 `Constructor.prototype` は既定で `constructor` という自身のプロパティを 1 つ持ち、これがコンストラクター関数自身を参照します。つまり、 `Box.prototype.constructor === Box` になります。これにより、あらゆるインスタンスから元のコンストラクターにアクセスできるようになります。

**メモ:** コンストラクター関数からプリミティブでない値が返された場合、その値が `new` 式の結果となります。この場合、`[[Prototype]]`は正しく結び付けられていないかもしれませんが、実際にはあまり起こらないはずです。

上記のコンストラクター関数は、クラスで書き直すことができます。

```js
class Box {
  constructor(value) {
    this.value = value;
  }

  // Box.prototype に作成されるメソッド
  getValue() {
    return this.value;
  }
}
```

クラスはコンストラクター関数の上の糖衣構文です。つまり、すべてのインスタンスの動作を変更するために `Box.prototype` を操作することができます。しかし、クラスは基本的なプロトタイプ機構を抽象化するように設計されているので、このチュートリアルではより軽量なコンストラクター関数の構文を使用して、プロトタイプがどのように動作するかを完全に示すことにします。

`Box.prototype` はすべてのインスタンスの `[[Prototype]]` と同じオブジェクトを参照しているので、 `Box.prototype` を変更することですべてのインスタンスの振る舞いを変更することができます。

```js
function Box(value) {
  this.value = value;
}
Box.prototype.getValue = function () {
  return this.value;
};
const box = new Box(1);

// インスタンス作成後に Box.prototype を変更
Box.prototype.getValue = function () {
  return this.value + 1;
};
box.getValue(); // 2
```

補足すると、 `Constructor.prototype` の再割り当て (`Constructor.prototype = ...`) は、 2 つの理由から悪い考えと言えます。

* 再割り当て前に作成されたインスタンスの `[[Prototype]]` は、再割り当て後に作成されたインスタンスの `[[Prototype]]` とは別のオブジェクトを参照するようになり、一方の `[[Prototype]]` を変更しても、もう一方は変更されません。
* `constructor` プロパティを手動で設定し直さない限り、コンストラクター関数は `instance.constructor` から辿ることができなくなり、ユーザーの期待を裏切る可能性があります。いくつかの組み込み演算子は `constructor` プロパティも読み込むので、それが設定されていない場合は期待通りに動作しない可能性があります。

`Constructor.prototype` はインスタンスを作成するときにのみ有用です。 `Constructor.[[Prototype]]` とは関係ありません。コンストラクター関数の自身のプロトタイプである `Function.prototype`、つまり、 `Object.getPrototypeOf(Constructor) === Function.prototype` とは関係がありません。

### リテラルの暗黙的なコンストラクター

JavaScript のいくつかのリテラル構文は、暗黙のうちに `[[Prototype]]` を設定したインスタンスを作成します。例えば次のようになります。

```js
// オブジェクトのリテラル（`\_\_proto\_\_` キーを持たない）には、
// 自動的に `Object.prototype` が `[[Prototype]]` として設定されます
const object = { a: 1 };
Object.getPrototypeOf(object) === Object.prototype; // true

// 配列リテラルでは、自動的に `Array.prototype` が `[[Prototype]]` となります
const array = [1, 2, 3];
Object.getPrototypeOf(array) === Array.prototype; // true

// RegExp リテラルでは、自動的に `RegExp.prototype` as their `[[Prototype]]`
const regexp = /abc/;
Object.getPrototypeOf(regexp) === RegExp.prototype; // true
```

「脱糖」してコンストラクターの形にすることができます。

```js
const array = new Array(1, 2, 3);
const regexp = new RegExp("abc");
```

例えば、 `map()` のような「配列のメソッド」は、単に `Array.prototype` で定義されたメソッドであり、そのため、すべての配列のインスタンスで自動的に利用することができます。

**警告:** かつて使用されていた誤った使い方が 1 つあります。 `Object.prototype` または他の組み込みプロトタイプのいずれかを拡張することです。この誤った使い方の例として、`Array.prototype.myMethod = function () {...}` を定義して、すべての配列インスタンスで `myMethod` できるようにするというものがあります。

この誤った使い方は、「モンキーパッチ」と呼ばれています。モンキーパッチは前方互換性を損なう危険性があり、将来、言語がこのメソッドを別のシグネチャで追加した場合、コードが壊れてしまうからです。 SmooshGate のような事件を引き起こし、 JavaScript が「ウェブを壊さない」ようにしようとするため、言語の進歩にとって非常に厄介な存在となりえます。

組み込みのプロトタイプを拡張する理由として**唯一の**良いものは、新しい JavaScript エンジンの機能、たとえば `Array.prototype.forEach` などをバックポートすることです。

歴史的な理由により、いくつかの組み込みコンストラクターの `prototype` プロパティはインスタンスそのものであることに注目すると興味深いかもしれません。例えば、 `Number.prototype` は数字の 0、 `Array.prototype` は空の配列、 `RegExp.prototype` は `/(?:)/` となります。

```js
Number.prototype + 1; // 1
Array.prototype.map((x) => x + 1); // []
String.prototype + "a"; // "a"
RegExp.prototype.source; // "(?:)"
Function.prototype(); // Function.prototype はそれ自身では何もしない関数
```

しかし、ユーザー定義されたコンストラクターや、 `Map` のような現代のコンストラクターでは、このようなことは起こりません。

```js
Map.prototype.get(1);
// Uncaught TypeError: get method called on incompatible Map.prototype
```

### より長い継承のチェーンの構築

`Constructor.prototype` プロパティは、コンストラクターのインスタンスの `[[Prototype]]` となり、 `Constructor.prototype` 自身の `[[Prototype]]` も含めてそのままの形で表示されます。既定で、 `Constructor.prototype` は *プレーンオブジェクト* です。つまり、 `Object.getPrototypeOf(Constructor.prototype) === Object.prototype` になります。唯一の例外は `Object.prototype` 自身で、その `[[Prototype]]` は `null` です - つまり、 `Object.getPrototypeOf(Object.prototype) === null` ということです。したがって、典型的なコンストラクターは以下のようなプロトタイプチェーンを構築します。

```js
function Constructor() {}

const obj = new Constructor();
// obj ---> Constructor.prototype ---> Object.prototype ---> null
```

もっと長いプロトタイプチェーンを構築する場合は、 `Constructor.prototype` の `[[Prototype]]` を `Object.setPrototypeOf()` 関数で設定することができます。

```js
function Base() {}
function Derived() {}
// `Derived.prototype` の `[[Prototype]]` を
// `Base.prototype` に設定
Object.setPrototypeOf(Derived.prototype, Base.prototype);

const obj = new Derived();
// obj ---> Derived.prototype ---> Base.prototype ---> Object.prototype ---> null
```

クラスの用語では、これは `extends` 構文を使用するのと同じです。

```js
class Base {}
class Derived extends Base {}

const obj = new Derived();
// obj ---> Derived.prototype ---> Base.prototype ---> Object.prototype ---> null
```

また、古いコードで `Object.create` を使用しているものを見かけることがあります。しかし、これは `prototype` プロパティを再割り当てするため、以前にここで説明した理由から、悪しき習慣と言えます。

```js
function Base() {}
function Derived() {}
// `Derived.prototype` に `Base.prototype` を `[[Prototype]]` とした
// 新しいオブジェクトを再代入う
// これは使わないでください。 — 代わりに Object.setPrototypeOf で変更してください。
Derived.prototype = Object.create(Base.prototype);
```

プロトタイプの調査：より深く掘り下げてみる
---------------------

その裏側で何が起こっているのか、もう少し詳しく見てみましょう。

JavaScript では、前述したように、関数はプロパティを持つことができます。すべての関数は `prototype` という名前の特別なプロパティがあります。以下のコードは独立したものであることに注意してください（このウェブページには、以下のコード以外に他の JavaScript は存在しないと考えてよいでしょう）。最高の学習体験をするためには、コンソールを開き、「コンソール」タブに移動して、以下の JavaScript コードをコピー＆ペーストし、 Enter/Return キーを押して実行することを強くお勧めします。（コンソールは、ほとんどのウェブブラウザーの開発者ツールに記載されています。詳しい情報は Firefox 開発者ツール、Chrome 開発者ツール、Edge 開発者ツールを参照してください。

```js
function doSomething() {}
console.log(doSomething.prototype);
// 関数をどのように宣言するかは問題ではありません。
// JavaScript の関数は常に既定のプロトタイププロパティを
// 保有します。ただし、例外が 1 つあります。アロー関数は
// 既定のプロトタイププロパティを持ちません。
const doSomethingFromArrowFunction = () => {};
console.log(doSomethingFromArrowFunction.prototype);
```

上で見たように、`doSomething()` は既定で `prototype` プロパティを所持しており、コンソールに表示されているようになります。このコードの実行後、コンソールにはこのようなオブジェクトが表示されているはずです。
```

{
  constructor: ƒ doSomething(),
  [[Prototype]]: {
    constructor: ƒ Object(),
    hasOwnProperty: ƒ hasOwnProperty(),
    isPrototypeOf: ƒ isPrototypeOf(),
    propertyIsEnumerable: ƒ propertyIsEnumerable(),
    toLocaleString: ƒ toLocaleString(),
    toString: ƒ toString(),
    valueOf: ƒ valueOf()
  }
}
```

**メモ:** Chrome のコンソールでは、 `[[Prototype]]` を使用してオブジェクトのプロトタイプを示しており、仕様の用語に従っています。Firefoxでは `<prototype>` を使用しています。一貫性を保つために、ここでは `[[Prototype]]` を使用します。

以下のように、 `doSomething()` のプロトタイプにプロパティを追加することができます。

```js
function doSomething() {}
doSomething.prototype.foo = "bar";
console.log(doSomething.prototype);
```

結果は次の通りです。
```

{
  foo: "bar",
  constructor: ƒ doSomething(),
  [[Prototype]]: {
    constructor: ƒ Object(),
    hasOwnProperty: ƒ hasOwnProperty(),
    isPrototypeOf: ƒ isPrototypeOf(),
    propertyIsEnumerable: ƒ propertyIsEnumerable(),
    toLocaleString: ƒ toLocaleString(),
    toString: ƒ toString(),
    valueOf: ƒ valueOf()
  }
}
```

ここで、 `new` 演算子を使用して、このプロトタイプを元に `doSomething()` のインスタンスを作成することができます。 new 演算子を使用するには、関数を呼び出す際に `new` という接頭辞を付ける以外は通常通り行います。 `new` 演算子を用いて関数を呼び出すと、その関数のインスタンスであるオブジェクトを返します。そして、このオブジェクトにプロパティを追加することができます。

以下のコードを試してみてください。

```js
function doSomething() {}
doSomething.prototype.foo = "bar"; // プロトタイプにプロパティを追加
const doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value"; // オブジェクトにプロパティを追加
console.log(doSomeInstancing);
```

この結果、以下のような出力が得られます。
```

{
  prop: "some value",
  [[Prototype]]: {
    foo: "bar",
    constructor: ƒ doSomething(),
    [[Prototype]]: {
      constructor: ƒ Object(),
      hasOwnProperty: ƒ hasOwnProperty(),
      isPrototypeOf: ƒ isPrototypeOf(),
      propertyIsEnumerable: ƒ propertyIsEnumerable(),
      toLocaleString: ƒ toLocaleString(),
      toString: ƒ toString(),
      valueOf: ƒ valueOf()
    }
  }
}
```

上で見たように、`doSomeInstancing`の `[[Prototype]]` は `doSomething.prototype` です。しかし、これは何をするものなのでしょうか？ `doSomeInstancing` のプロパティにアクセスするとき、ランタイムはまず `doSomeInstancing` がそのプロパティを持っているかどうかを調べます。

もし `doSomeInstancing` がそのプロパティを持っていない場合、ランタイムは `doSomeInstancing.[[Prototype]]` （別名 `doSomething.prototype`）でそのプロパティを探します。もし、 `doSomeInstancing.[[Prototype]]` が探しているプロパティを保有している場合、 `doSomeInstancing.[[Prototype]]` 上のそのプロパティが使用されます。

そうでなければ、 `doSomeInstancing.[[Prototype]]` がそのプロパティを持っていなかった場合、 `doSomeInstancing.[[Prototype]].[[Prototype]]` がそのプロパティを調べることになります。既定で、任意の関数の `prototype` プロパティの `[[Prototype]]` は `Object.prototype` です。そこで、`doSomeInstancing.[[Prototype]].[[Prototype]]` （別名 `doSomething.prototype.[[Prototype]]` （別名 `Object.prototype`））は、検索対象のプロパティを調べることになります。

もしそのプロパティが `doSomeInstancing.[[Prototype]].[[Prototype]]` の中に見つからなければ、 `doSomeInstancing.[[Prototype]].[[Prototype]].[[Prototype]]` を探します。しかし、 `doSomeInstancing.[[Prototype]].[[Prototype]].[[Prototype]]` は存在しません。なぜなら、 `Object.prototype.[[Prototype]]` は `null` であるからです。そして、 `[[Prototype]]` のプロトタイプチェーン全体が調べられた後、ランタイムはプロパティが存在しないことを主張し、プロパティの値は `undefined` であると結論付けます。

コンソールにもう少しコードを入力してみましょう。

```js
function doSomething() {}
doSomething.prototype.foo = "bar";
const doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value";
console.log("doSomeInstancing.prop: ", doSomeInstancing.prop);
console.log("doSomeInstancing.foo: ", doSomeInstancing.foo);
console.log("doSomething.prop: ", doSomething.prop);
console.log("doSomething.foo: ", doSomething.foo);
console.log("doSomething.prototype.prop:", doSomething.prototype.prop);
console.log("doSomething.prototype.foo: ", doSomething.prototype.foo);
```

この結果は以下のようになります。
```

doSomeInstancing.prop:      some value
doSomeInstancing.foo:       bar
doSomething.prop:           undefined
doSomething.foo:            undefined
doSomething.prototype.prop: undefined
doSomething.prototype.foo:  bar
```

プロトタイプチェーンを作成・変更する様々な方法
-----------------------

これまで、オブジェクトを作成し、そのプロトタイプチェーンを変更する多くの方法に触れてきました。それぞれの手法の長所と短所を比較しながら、さまざまな方法を体系的にまとめていきます。

### 構文で作成されたオブジェクト

```js
const o = { a: 1 };
// オブジェクト o を新しく作成すると、 Object.prototype が [[Prototype]] になります。
// Object.prototype はプロトタイプが null です。
// o ---> Object.prototype ---> null

const b = ["yo", "whadup", "?"];
// 配列は Array.prototype を継承します。
// （indexOf, forEach, などのメソッドがあります。）
// プロトタイプチェーンは次のようになります。
// b ---> Array.prototype ---> Object.prototype ---> null

function f() {
  return 2;
}
// 関数は Function.prototype を継承します。
// （call, bind, などのメソッドがあります。）
// f ---> Function.prototype ---> Object.prototype ---> null

const p = { b: 2, \_\_proto\_\_: o };
// 新しく作成したオブジェクトの [[Prototype]] は、 \_\_proto\_\_ リテラル
// プロパティを介して指し示すことができます。
// （Object.prototype.\_\_proto\_\_ アクセサーと混同しないでください。）
// p ---> o ---> Object.prototype ---> null
```

`__proto__` キーをオブジェクト初期化子で使用する方法の長所と短所| 長所 | 現代のすべてのエンジンで対応しています。オブジェクトでないものを `__proto__` キーで指しても、例外を発生させずに暗黙に失敗するだけです。 `Object.prototype.__proto__` セッターとは対照的に、オブジェクトリテラル初期化子の `__proto__` は標準化、最適化されており、 `Object.create` よりもパフォーマンスが高くなることさえあります。オブジェクトの作成時に自分自身で追加のプロパティを宣言することは、 `Object.create` よりも人間工学的に優れています。 |
| 短所 | IE10 以下では対応していません。 `Object.prototype.__proto__` アクセサーと混同しやすいので、違いを知らない人は注意してください。 |

### コンストラクター関数で

```js
function Graph() {
  this.vertices = [];
  this.edges = [];
}

Graph.prototype.addVertex = function (v) {
  this.vertices.push(v);
};

const g = new Graph();
// g は自身のプロパティ 'vertices' と 'edges' を持つオブジェクトです。
// g.[[Prototype]] は Graph.prototype の new Graph() が実行されたときの値です。
```

コンストラクター関数を使用する方法の長所と短所| 長所 | IE 5.5 までさかのぼり、すべてのエンジンにおいて対応しています。また、とても高速で、とても標準的であり、とても JIT で最適化できます。 |
| 短所 | * このメソッドを使用するためには、当該関数を初期化する必要があります。この初期化の際に、コンストラクターはオブジェクトごとに生成する必要がある固有の情報を格納することがあります。この固有の情報は一度しか生成されないため、問題が発生する可能性があります。
* コンストラクターの初期化により、不要なメソッドがオブジェクトに搭載される可能性があります。

どちらも、実際には一般的に問題になることはありません。 |

### Object.create() で

`Object.create()` を呼び出すと、新しいオブジェクトが作成されます。このオブジェクトの `[[Prototype]]` がこの関数の最初の引数になります。

```js
const a = { a: 1 };
// a ---> Object.prototype ---> null

const b = Object.create(a);
// b ---> a ---> Object.prototype ---> null
console.log(b.a); // 1 （継承）

const c = Object.create(b);
// c ---> b ---> a ---> Object.prototype ---> null

const d = Object.create(null);
// d ---> null （d はプロトタイプとして直接 null を持つオブジェクト）
console.log(d.hasOwnProperty);
// undefined。 Object.prototype を継承していないため
```

`Object.create` の長所と短所| 長所 | 現代のすべてのエンジンで対応しています。オブジェクトの作成時に `[[Prototype]]` を直接設定することができ、ランタイムがオブジェクトをさらに最適化することを可能にします。また、`Object.create(null)`を使用して、プロトタイプを持たないオブジェクトの作成も可能です。 |
| 短所 | IE8 以下では対応していません。しかし、 Microsoft は IE8 以下を搭載したシステムの拡張サポートを終了しているため、ほとんどのアプリケーションでは気にする必要はないでしょう。さらに、 2 つ目の引数を使用した場合、オブジェクト記述子のプロパティがそれぞれ別個の記述子オブジェクトを持つため、オブジェクトの初期化が遅くなり、パフォーマンスのブラックホールとなる可能性があります。オブジェクトの形成する形で何十万ものオブジェクト記述子を扱う場合、その遅延時間は深刻な問題になるかもしれません。 |

### クラスで

```js
class Polygon {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

class Square extends Polygon {
  constructor(sideLength) {
    super(sideLength, sideLength);
  }

  get area() {
    return this.height \* this.width;
  }

  set sideLength(newLength) {
    this.height = newLength;
    this.width = newLength;
  }
}

const square = new Square(2);
// square ---> Square.prototype ---> Polygon.prototype ---> Object.prototype ---> null
```

クラスの長所と短所| 長所 | 現代のすべてのエンジンで対応しています。とても高い可読性と保守性。プライベートプロパティは、プロトタイプ継承において些細な置き換えのない機能です。 |
| 短所 | クラス、特にプライベートプロパティを持つクラスは、従来のものに比べて最適化されていません（ただし、エンジンの実装者はこの点を改善するよう作業しています）。古い環境では対応しておらず、実運用でクラスを使用するためには通常トランスパイラーが必要です。 |

### Object.setPrototypeOf() で

上記のすべてのメソッドはオブジェクト生成時にプロトタイプチェーンを設定するのに対し、 `Object.setPrototypeOf()` は既存のオブジェクトの `[[Prototype]]` 内部プロパティを変更することができます。

```js
const obj = { a: 1 };
const anotherObj = { b: 2 };
Object.setPrototypeOf(obj, anotherObj);
// obj ---> anotherObj ---> Object.prototype ---> null
```

`Object.setPrototypeOf` の長所と短所| 長所 | 現代のすべてのエンジンで対応しています。オブジェクトのプロトタイプを動的に操作することができ、`Object.create(null)` で作成したプロトタイプのないオブジェクトにプロトタイプを強制的に作成することもできます。 |
| 短所 | パフォーマンスが悪い。オブジェクト生成時にプロトタイプを設定することが可能であれば、避けるべきです。多くのエンジンはプロトタイプを最適化し、インスタンスを呼び出すときにメモリー上のメソッドの位置を事前に推測しようとします。しかし、プロトタイプを動的に設定すると、これらの最適化がすべて中断されます。これは、エンジンによっては、コードを仕様通りに動作させるために、最適化を解除して再コンパイルするような事態を発生させるかもしれません。IE8 以前は対応していません。 |

### \_\_proto\_\_ アクセサーで

すべてのオブジェクトは `Object.prototype.__proto__` セッターを継承しており、これを使用して既存のオブジェクトの `[[Prototype]]` を設定することができます（`__proto__` キーがオブジェクトでオーバーライドされていない場合）。

**警告:** `Object.prototype.__proto__` アクセサーは**標準外**で非推奨です。 代わりに、常に `Object.setPrototypeOf` を使用する必要があります。

```js
const obj = {};
// 使用しないでください。例示のためのものです。
obj.__proto__ = { barProp: "bar val" };
obj.__proto__.__proto__ = { fooProp: "foo val" };
console.log(obj.fooProp);
console.log(obj.barProp);
```

`__proto__` プロパティの設定の長所と短所| 長所 | 現代のすべてのエンジンで対応しています。`__proto__` をオブジェクトでないものに設定すると、暗黙に失敗するだけです。例外は発生しません。 |
| 短所 | 非公認かつ非推奨です。多くのエンジンはプロトタイプを最適化し、インスタンスを呼び出すときにメモリー内のメソッドの位置を事前に推測しようとします。しかし、プロトタイプを動的に設定すると、これらの最適化をすべて中断し、一部のエンジンでは、コードを最適化しないように再コンパイルして、仕様どおりに動作するように強制することさえあります。 IE10 以下では対応していません。 `__proto__` セッターは、通常は省略可能なので、すべてのプラットフォームで動作するわけではありません。代わりに、常に `Object.setPrototypeOf` を使用してください。 |

パフォーマンス
-------

プロトタイプチェーンの上位にあるプロパティを参照する時間は、パフォーマンスにマイナスの影響を与える可能性があり、パフォーマンスが重要視されるコードでは、この影響は大きいかもしれません。さらに、存在しないプロパティにアクセスしようとすると、常にプロトタイプチェーンを完全に縦断することになります。

また、オブジェクトのプロパティを反復処理するときに、プロトタイプチェーン上にある**すべての** 列挙可能なプロパティが列挙されることになります。オブジェクトがプロトタイプチェーンのどこかではなく、\_自分自身に定義されたプロパティを持っているかどうかを調べるには、 `hasOwnProperty` または `Object.hasOwn` メソッドを使用しなければいけません。 `[[Prototype]]` として `null` を持つオブジェクトを除くすべてのオブジェクトは、 `hasOwnProperty` を `Object.prototype` から継承ししています。ただし、プロトタイプチェーンのさらに下でオーバーライドされている場合を除きます。具体的な例を挙げるために、上記のグラフの例のようなコードを使って説明しましょう。

```js
console.log(g.hasOwnProperty("vertices"));
// true

console.log(Object.hasOwn(g, "vertices"));
// true

console.log(g.hasOwnProperty("nope"));
// false

console.log(Object.hasOwn(g, "nope"));
// false

console.log(g.hasOwnProperty("addVertex"));
// false

console.log(Object.hasOwn(g, "addVertex"));
// false

console.log(Object.getPrototypeOf(g).hasOwnProperty("addVertex"));
// true
```

注: あるプロパティが `undefined` であるかどうかを調べるだけでは十分では**ありません**。プロパティがしっかり存在していて、たまたま `undefined` に設定されているだけかもしれないからです。

まとめ
---

JavaScript は Java や C++ から来た開発者にとっては少し戸惑うかもしれません。なぜなら、JavaScript はすべて動的で、すべて実行時であり、静的な型はまったくないからです。すべてがオブジェクト（インスタンス）か関数（コンストラクター）であり、関数自体も `Function` コンストラクターのインスタンスです。構文の構成要素である「クラス」でさえ、実行時にはコンストラクター関数に過ぎないのです。

JavaScript のすべてのコンストラクター関数は `prototype` という特別なプロパティを保有しており、このプロパティは `new` 演算子と組み合わせて動作します。プロトタイプオブジェクトへの参照は、新しいインスタンスの内部プロパティ `[[Prototype]]` にコピーされます。例えば、 `const a1 = new A()` とすると、 JavaScript は（オブジェクトをメモリー上で作成した後、 `this` を定義した関数 `A()` を実行する前に） `a1.[[Prototype]] = A.prototype` を設定します。その後、インスタンスのプロパティにアクセスするとき、 JavaScript はまずそのオブジェクトに直接存在するかどうかを調べ、存在しない場合は `[[Prototype]]` を探します。すなわち、 `a1.doSomething`, `Object.getPrototypeOf(a1).doSomething`, `Object.getPrototypeOf(Object.getPrototypeOf(a1)).doSomething` など、見つかるまで、あるいは `Object.getPrototypeOf` が `null` を返すまで再帰的に調べられます。これは、 `prototype` で定義されたすべてのプロパティが、事実上すべてのインスタンスで共有され、後で`prototype`の一部を変更しても、その変更が既存のすべてのインスタンスに現れることを意味します。

上の例で、`const a1 = new A(); const a2 = new A();` とした場合、`a1.doSomething` は実際には `Object.getPrototypeOf(a1).doSomething` となります - この値は、定義した `A.prototype` と同じものです。 つまり、 `Object.getPrototypeOf(a1).doSomething === Object.getPrototypeOf(a2).doSomething === A.prototype.doSomething` と定義したことになります。

プロトタイプ継承モデルを使用する複雑なコードを書く前に、プロトタイプ継承モデルを理解することが重要です。また、コード中のプロトタイプチェーンの長さを意識し、パフォーマンスの問題を避けるために、必要に応じてそれを分割してください。さらに、ネイティブのプロトタイプは、新しい JavaScript の機能との互換性をとるためでない限り、**決して**拡張してはいけません。


メモリー管理 - JavaScript

メモリー管理
======

C のような低水準言語には、`malloc()` や `free()` のような低水準のメモリー管理プリミティブがあります。これに対して JavaScript では、オブジェクトを作成するときにメモリーを自動的に確保し、使用しなくなったらメモリーを解放します（*ガーベジコレクション*）。この自動性が混乱の元になる可能性があります。メモリー管理について心配する必要がないという誤った印象を開発者に与える可能性があります。

メモリーライフサイクル
-----------

プログラミング言語に関係なく、メモリーのライフサイクルはほぼいつも同じです。

1. 必要なメモリーを割り当てる
2. 割り当てられたメモリーを使用する（読み込む, 書き込む）
3. 必要なくなったら、割り当てられたメモリーを解放する

2 に関してはすべての言語で明示的に行われます。1 と 3 は低水準の言語では明示的ですが、JavaScript のような高水準言語では、ほとんどの場合暗黙的に行われます。

### JavaScript での割り当て

#### 値の初期化

割り当てでプログラマーを悩まさないために、JavaScript では値を宣言したときと同時にメモリーの割り当ても行われます。

```js
const n = 123; // 数値を格納するメモリーが割り当てられます
const s = "azerty"; // 文字列を格納するメモリーが割り当てられます

const o = {
  a: 1,
  b: null,
}; // オブジェクトとそれに含まれる値を格納するためのメモリーが割り当てられます

// （オブジェクトの例と同じように）配列とそれに含まれる値を格納するための
// メモリーが割り当てられます
const a = [1, null, "abra"];

function f(a) {
  return a + 2;
} // 関数を格納するメモリーが割り当てられます (関数は呼び出し可能なオブジェクトです)

// 関数式でもメモリーの割り当てが行われます
someElement.addEventListener(
  "click",
  () => {
    someElement.style.backgroundColor = "blue";
  },
  false,
);
```

#### 関数呼び出しを介して割り当て

一部の関数呼び出しでは、オブジェクトの割り当てが発生します。

```js
const d = new Date(); // Date オブジェクトの割り当て

const e = document.createElement("div"); // DOM要素の割り当て
```

いくつかのメソッドは、新しい値またはオブジェクトを割り当てます:

```js
const s = "azerty";
const s2 = s.substr(0, 3); // s2 は新しい文字列
// JavaScript では文字列は変更不可の値なので、
// メモリーの割当を行わないと思うかもしれません。
// しかし実際には [0, 3] の範囲の文字列が割り当てられます。

const a = ["ouais ouais", "nan nan"];
const a2 = ["generation", "nan nan"];
const a3 = a.concat(a2);
// a, a2 の内容を繋ぎ合わせた 4 要素の配列が作成されました
```

### 値の使用

値を使用することは、基本的に割り当てられたメモリーに読み書きすることを意味します。これは変数やオブジェクトの値を読み書きすることや引数を関数に渡すことによって行われます。

### メモリーが不要になったときの解放

メモリー管理の問題のほとんどは、この段階に来ます。ここで最も難しい作業は、「割り当てられたメモリーが、必要とされなくなるときを見出すすることです。

プログラム内のどこで、そのようなメモリーの断片が不要になって解放する必要があるかを決定するには、開発者による判断が必要なことが多いです。

一部の高水準言語、例えば JavaScript は、ガベージコレクション (GC) として知られる自動メモリー管理の方式を利用しています。ガベージコレクターの目的は、メモリーの割り当てを監視し、割り当てられたメモリーのブロックができなくなったときに判断し、それを回収することです。特定のメモリーがまだ必要かどうかを判断する一般的な問題は決定不能であるため、この自動処理は近似的なものです。

ガベージコレクション
----------

上述の通り、あるメモリーが「必要なくなった」かどうかを自動的に知るという普遍的問題は、決定不能です。そのため、ガベージコレクションのこの普遍的問題に対する解決策には制限があります。この節では、ガベージコレクションの主なアルゴリズムとその限界を理解するために必要な概念を説明します。

### 参照

ガベージコレクションアルゴリズムが依存している主な概念は、参照 (reference) の概念です。メモリー管理の文脈では、あるオブジェクトが別のオブジェクトに（明示的にであれ、暗黙的にであれ）アクセスできるとき、前者が後者を参照していると言います。例えば、JavaScript オブジェクトは自身の プロトタイプ（暗黙的な参照）とプロパティ値（明示的な参照）への参照を持ちます。

ここでは、「オブジェクト」の概念は通常の JavaScript オブジェクトよりも広い概念として用いられており、また、関数のスコープ（もしくは、グローバルレキシカルスコープ）を含みます。

### 参照カウントのガベージコレクション

**メモ:** 現代のブラウザーで、ガベージコレクションに参照カウントを使用しているものはもうありません。

これは、最も素朴なガベージコレクションアルゴリズムです。このアルゴリズムは、「あるオブジェクトが必要なくなった」ことを、「あるオブジェクトがその他のオブジェクトから参照されていない」ことと定義します。あるオブジェクトは、それに対する参照がゼロの時にガベージコレクション可能であると見なされます。

#### 例

```js
let x = {
  a: {
    b: 2,
  },
};
// 2 個のオブジェクトが作成されました。一方はもう一方のプロパティとして参照されています。
// もう一方は変数 'x' に代入されているため、こちらも同じく参照されています。
// 明らかに、どちらのオブジェクトもガベージコレクションの対象になりません。

let y = x;
// 変数 'y' は、このオブジェクトを参照する 2 つ目のものです。

x = 1;
// これで、元々 'x' にあったオブジェクトは、変数 'y' によって固有の参照が
// 具現化されたことになります。

let z = y.a;
// オブジェクトのプロパティ 'a' への参照です。
// これで、このオブジェクトは2つの参照先を持つことになりました。
// 1 つはプロパティとして、もう 1 つは変数 'z' としてです。

y = "mozilla";
// もともと 'x' にあったオブジェクトは、これで参照するオブジェクトが
// ゼロになりました。ガベージコレクションすることができます。
// しかし、そのプロパティ 'a' はまだ変数 'z' によって参照されているため、
// 解放することはできません。

z = null;
// もともと x にあったオブジェクトのプロパティ 'a' は、それへの参照が
// ゼロです。ガベージコレクションすることができます。
```

循環参照があると、制限があります。以下の例では、互いに参照するプロパティを持つ 2 つのオブジェクトが作成され、循環を作り出しています。これらのオブジェクトは、関数の呼び出しが完全に終了すると、スコープ外に出ます。この点で、オブジェクトは不要となり、割り当てられたメモリーを回収する必要があります。しかし、参照カウントアルゴリズムは、2 つのオブジェクトがそれぞれ少なくとも 1 つの参照点を持っているため、それらを再生可能とは見なさず、結果的にどちらもガベージコレクションにマークされないことになります。参照するオブジェクトは、メモリーリークの一般的な発生させる原因です。

```js
function f() {
  const x = {};
  const y = {};
  x.a = y; // x references y
  y.a = x; // y references x

  return "azerty";
}

f();
```

### マークアンドスイープアルゴリズム

このアルゴリズムは、「あるオブジェクトが必要なくなった」ことを、「あるオブジェクトが到達不能である」ことと定義します。

このアルゴリズムは、*root* と呼ばれるオブジェクトの集合についての知識を前提としています（JavaScript では、root はグローバルオブジェクトです）。定期的に、ガベージコレクターは、これらの root から開始し、これらの root から参照されるすべてのオブジェクト、それから、これらの中から参照されるすべてのオブジェクトなどを見つけます。root から開始すると、ガベージコレクターは、すべての*到達可能*オブジェクトを見つけ、すべての到達不能なオブジェクトをガベージコレクトします。

「あるオブジェクトが参照を持たない」ということは、そのオブジェクトは到達不能であるということなので、このアルゴリズムは前述のものよりも優れています。循環で見たように、逆は正しくありません。

現在、すべての現代的なブラウザーでは、マークアンドスイープ式のガベージコレクターを持っています。過去数年間で JavaScript のガベージコレクション（世代別/インクリメンタル/並行/並列ガベージコレクション）の分野で行われたすべての改善は、このアルゴリズムの実装の改善であって、ガベージコレクションアルゴリズム自体に対する改善でも、「オブジェクトが必要とされなくなった」と扱う基準を変えるものでもありません。

この手法の直接的な好ましいことは、循環が問題にならなくなることです。上の最初の例では、関数呼び出しを返した後、2つのオブジェクトは、グローバルオブジェクトから到達可能などのリソースからも参照されなくなりました。その結果、これらはガベージコレクターによって到達できないことが分かり、割り当てられたメモリーが再利用されることになります。

しかし、ガベージコレクションを手動で制御することができないのは変わりません。いつ、どんなメモリーを解放するかを手動で決めることができれば便利な時があります。オブジェクトのメモリーを解放するためには、明示的に到達できないようにする必要があります。また、JavaScript ではプログラムによってガベージコレクションを発生させることはできませんし、エンジンがオプトインフラグで API を公開することはあっても、コア言語の中で発生することはないでしょう。

エンジンのメモリーモデルを構成する
-----------------

JavaScript エンジンは通常、メモリーモデルを公開するフラグを提供します。例えば、Node.js は、メモリーの問題を構成し、デバッグするために、基盤となるV8のメカニズムを公開する追加オプションやツールを提供しています。この設定は、ブラウザーでは利用できないかもしれませんし、ウェブページでは（HTTP ヘッダーなどを通じて）さらに利用できないかもしれません。

利用できるヒープメモリーの最大量は、フラグで増やすことができます。

```bash
node --max-old-space-size=6000 index.js
```

また、フラグや Chrome デバッガーを用いて、メモリーの問題をデバッグするためにガベージコレクターを公開することができます。

```bash
node --expose-gc --inspect index.js
```

メモリー管理を支援するデータ構造
----------------

JavaScript はガベージコレクター API を直接公開していませんが、この言語はガベージコレクションを間接的に監視するデータ構造をいくつか提供しており、メモリー使用量を管理するために使用することができます。

### WeakMap と WeakSet

`WeakMap` と `WeakSet` には、データ構造がよく似た弱くない方の API、`Map` と `Set` があります。`WeakMap` はキーと値のペアの集合を、`WeakSet` は固有の値の集合を保持することができ、どちらも追加、削除、問い合わせの実行が可能である。

`WeakMap` と `WeakSet` は、*weakly held* 値の概念から取った名前です。`x` が `y` によって弱く保持されている場合、`x` の値には `y` を介してアクセスできますが、マークアンドスイープアルゴリズムでは、何か強く保持するものがなければ `x` に到達できるとは考えないということを意味しています。ここで議論するものを除くほとんどのデータ構造は、合格したオブジェクトをいつでも取り出せるように強く保持します。`WeakMap` と `WeakSet` のキーは、プログラム内でそのキーを参照しているものがない限り、ガベージコレクションすることができます（`WeakMap` オブジェクトの場合、値もガベージコレクションの対象となります）。これは、2 つの特徴によって確実に保持されます。

* `WeakMap` と `WeakSet` はオブジェクトのみを格納することができます。これは、オブジェクトだけがガベージコレクションされるからです。プリミティブ値は常に偽造することができるので（つまり、`1 === 1` でも `{} !== {}`）、永遠に集合に留まることになります。
* `WeakMap` と `WeakSet` は反復可能なオブジェクトではありません。このため、`Array.from(map.keys()).length`を使用してオブジェクトの生存率を監視したり、ガベージコレクションの対象となるような任意のキーを取得することができません。（ガベージコレクションは使用可能な限り不可視であるべきです。）

`WeakMap` や `WeakSet` の典型的な説明（上記のようなもの）では、キーが最初にガベージコレクションされ、値も同様にガベージコレクションのために無料になることが暗示されています。しかし、値をキーに参照する場合を考えてみましょう。

```js
const wm = new WeakMap();
const key = {};
wm.set(key, { key });
// これで `key` はガベージコレクションできなくなりました。
// 値はキーへの参照を保持し、値はマップに強く保持されて
// いるからです。
```

もし `key` が実際の参照として格納されると、循環参照を作成し、他に `key` を参照するものがない場合でも、キーと値の両方をガベージコレクションの対象外にしてしまいます。もし `key` がガベージコレクションされると、具体的なある瞬間に `value.key` が存在しないアドレスを指すことになり、これは不正な状態であるため。これを修正するために、`WeakMap` と `WeakSet` の項目は実際の参照ではなく、マークアンドスイープ機構を強化したエフェメロンです。Barros らは、このアルゴリズムの良い概要を提供しています（4 ページ目）。一段落を引用します。

>
> エフェメロンは弱いペアを改良したもので、鍵も値も弱いとも強いとも分類できません。鍵の接続性は値の接続性を決定しますが、値の接続性は鍵の接続性には影響しません。 […] ガベージコレクションがエフェメロンに対応する場合、2 つのフェーズ（マークとスイープ）ではなく、3 つのフェーズで発生します。）
>
>
>

大まかなメンタルモデルとして、`WeakMap` は以下のような実装だと考えてください。

**警告:** これはポリフィルではなく、エンジンで実装されている方法（ガベージコレクション機構にフックしている）にも近いものです。

```js
class MyWeakMap {
  #marker = Symbol("MyWeakMapData");
  get(key) {
    return key[this.#marker];
  }
  set(key, value) {
    key[this.#marker] = value;
  }
  has(key) {
    return this.#marker in key;
  }
  delete(key) {
    delete key[this.#marker];
  }
}
```

ご覧のように、`MyWeakMap`は実際にはキーの集合を保持することはありません。単に、合格した各オブジェクトにメタデータを追加するだけです。そして、そのオブジェクトはマークアンドスイープによってガベージコレクションされます。したがって、`WeakMap`内のキーを反復処理したり、`WeakMap`をクリアしたりすることはできない(これもキーコレクション全体の知識に頼っているからである)。

これらの API の詳細については、キー付きコレクション のガイドを参照してください。

### WeakRefs と FinalizationRegistry

**メモ:** `WeakRef` と `FinalizationRegistry` は、ガベージコレクション機構に直接触れる機会を提供します。実行時の意味づけが完全に保証されていないため、可能な限り使用しないでください。

オブジェクトを値とする変数はすべて、そのオブジェクトを参照しています。しかし、このような参照は *強い* ものであり、その存在によってガベージコレクタがそのオブジェクトを収集対象としてマークすることができなくなります。`WeakRef` は、オブジェクトへの弱い参照で、オブジェクトをガベージコレクションすることができ、かつオブジェクトが生きている間にそのコンテンツを読むことができるようにします。

`WeakRef` の用途として、文字列の URL を大きなオブジェクトに割り当てるキャッシュシステムがあります。そのために `WeakMap` を使用することはできません。なぜなら、`WeakMap` オブジェクトは *キー* を弱く保持しますが、*値* は弱く保持しないからです。キーにアクセスすれば、常に決定論的に値が取得されます（キーにアクセスしていることは、それがまだ生きていることを意味するからです）。ここで、キーに対して `undefined` を取得しても（対応する値が使えなくなった場合）、再計算すればよいので問題ありませんが、到達できないオブジェクトがキャッシュに残ってしまうのは困ります。この場合、通常の `Map` を使用しますが、各値は実際のオブジェクトの値ではなく、オブジェクトの `WeakRef` となります。

```js
function cached(getter) {
  // 文字列の URL から結果の WeakRef へ割り当てられたマップ
  const cache = new Map();
  return async (key) => {
    if (cache.has(key)) {
      return cache.get(key).deref();
    }
    const value = await getter(key);
    cache.set(key, new WeakRef(value));
    return value;
  };
}

const getImage = cached((url) => fetch(url).then((res) => res.blob()));
```

`FinalizationRegistry` は、ガベージコレクションを監視するさらに強力なメカニズムを提供します。これは、オブジェクトを登録し、それらがガベージコレクションされたときに通知されるようにするものです。例えば、上記のキャッシュシステムでは、Blob 自体が集合に無料であっても、それを保持する `WeakRef` オブジェクトはそうではなく、時間とともに `Map` に多くの無駄な項目が蓄積される可能性があります。`FinalizationRegistry`を使用することで、このような用途のクリーンアップを行うことができます。

```js
function cached(getter) {
  // 文字列の URL から結果の WeakRef へ割り当てられたマップ
  const cache = new Map();
  // 値がガベージコレクションされた後、毎回、キャッシュ内の
  // キーを引数にコールバックが呼び出され、キャッシュの項目を
  // 削除することができる
  const registry = new FinalizationRegistry((key) => {
    // 注: WeakRef が本当に空であることをテストすることが重要です。
    // さもなければ、このキーで新しいオブジェクトが追加され、その生きている新しい
    // オブジェクトが削除された後にコールバックが呼び出される可能性があります。
    if (!cache.get(key)?.deref()) {
      cache.delete(key);
    }
  });
  return async (key) => {
    if (cache.has(key)) {
      return cache.get(key).deref();
    }
    const value = await getter(key);
    cache.set(key, new WeakRef(value));
    registry.register(value, key);
    return value;
  };
}

const getImage = cached((url) => fetch(url).then((res) => res.blob()));
```

パフォーマンスとセキュリティの関係で、コールバックがいつ呼び出されるか、あるいはすべて呼び出されるかどうかは保証されていません。コールバックはクリーンアップにのみ使用すべきであり、しかも重要でないクリーンアップにのみ使用すべきです。他にも、`try...finally` で常に実行される `finally` ブロックで実行するなど、より決定的にリソースを管理する方法があります。`WeakRef` と `FinalizationRegistry` は、長時間実行するプログラムのメモリー使用量を最適化するためだけに存在します。

`WeakRef` および `FinalizationRegistry` の API について詳しくは、それぞれのリファレンスページを参照してください。


JavaScript のデータ型とデータ構造 - JavaScript

JavaScript のデータ型とデータ構造
======================

プログラミング言語には、どれにも組み込みデータ構造がありますが、ふつうは言語ごとに異なります。この記事では、JavaScript で使用可能な組み込みデータ構造の一覧と、他のデータ構造の構築にも使えるように、それらがどのような性質を持ち合わせているかについて述べることにします。

言語概要では、一般的なデータ型を同様にまとめていますが、もっと他の言語との比較も行っています。

動的かつ弱い型付け
---------

JavaScript は動的言語であり、動的型付けの言語です。JavaScript では、変数が直接的に特定のデータ型に関連付けられているわけではなく、どの変数にもあらゆる型の値を代入（および再代入）することができます。

```js
let foo = 42; // foo は数値型になった
foo = "bar"; // foo は文字列型になった
foo = true; // foo は論理型になった
```

JavaScriptは弱い型付けの言語でもあります。これは、処理に不一致の型が含まれる場合、型エラーを発生させるのではなく、暗黙の型変換を可能にすることを意味しています。

```js
const foo = 42; // foo は数値型
const result = foo + "1"; // JavaScript は、foo を文字列に変換し、他のオペランドと連結することができます
console.log(result); // 421
```

暗黙の型変換はとても便利ですが、開発者が変換を意図していなかったり、他の方向への変換（例えば、数値から文字列ではなく文字列から数値）を意図していた場合、足かせになる可能性があります。シンボルと長整数については、JavaScript は意図的に特定の暗黙の型変換を禁止してきました。

プリミティブ値
-------

オブジェクトを除くすべての型は、言語の最下層で直接表現される不変値を定義しています。これらの型の値を *プリミティブ値* と呼びます。

`null` を除くすべてのプリミティブ型は、`typeof` 演算子で確認することができます。`typeof null` は `"object"` を返すので、`null` であることを確認するには `=== null` を使用しなければなりません。

`null` と `undefined` を除くすべてのプリミティブ型には、対応するオブジェクトラッパー型があり、そのオブジェクトはプリミティブ値の操作を行うために有用なメソッドを提供しています。例えば、`Number` オブジェクトは `toExponential()` などのメソッドを提供しています。プリミティブ値に対してプロパティでアクセスすると、JavaScript は自動的に値を対応するラッパーオブジェクトにラップし、代わりにそのオブジェクトのプロパティにアクセスします。しかし、`null` や `undefined` のプロパティにアクセスすると `TypeError` 例外が発生するため、オプショナルチェーン演算子を導入する必要があります。

| 型 | `typeof` の返値 | オブジェクトラッパー |
| --- | --- | --- |
| Null 型 | `"object"` | なし |
| Undefined 型 | `"undefined"` | なし |
| 論理型 | `"boolean"` | `Boolean` |
| 数値型 | `"number"` | `Number` |
| 長整数型 | `"bigint"` | `BigInt` |
| 文字列型 | `"string"` | `String` |
| シンボル型 | `"symbol"` | `Symbol` |

オブジェクトラッパークラスのリファレンスページには、プリミティブ型そのものの意味づけの詳細な説明だけでなく、それぞれの型で利用できるメソッドやプロパティの詳細な情報が掲載されています。

### Null 型

Null 型には、値が `null` の 1 つしかありません。

### Undefined 型

Undefined 型には、値が `undefined` の 1 つしかありません。

概念的には、`undefined` は *値* がないことを示し、`null` は *オブジェクト* がないことを示します（`typeof null === "object"` であることの説明にもなるでしょう）。言語では通常、値がない場合は `undefined` が既定値となります。

* 値がない `return` 文 (`return;`) は、暗黙的に `undefined` を返します。
* 存在しないオブジェクト プロパティ (`obj.iDontExist`) にアクセスすると `undefined` が返されます。
* 初期化を伴わない変数宣言 (`let x;`) は、暗黙的にその変数を `undefined` に初期化します。
* `Array.prototype.find()` や `Map.prototype.get()` など、多くのメソッドは要素が見つからないと `undefined` を返します。

コア言語では、`null` が使用される頻度はかなり低くなっています。最も重要な場所は、プロトタイプチェーンの終わりです。その後、`Object.getPrototypeOf()`、`Object.create()` など、プロトタイプとやりとりするメソッドは `undefined` に代わり `null` を受け入れるか返します。

`null` はキーワードですが、`undefined` は通常の識別子で、たまたまグローバルプロパティであると見なされます。実際には、`undefined` は再定義されたり、シャドウ化されたりすることはないので、この違いは小さいです。

### 論理型

論理型 (`Boolean`) は論理の状態を表すもので、 `true` と `false` の 2 つの値があります。

論理値は通常、三項演算子、`if...else`、`while` などの条件操作に用いられます。

### 数値型

数値型 (`Number`) は IEEE 754 の倍精度浮動小数点形式の値です。2-1074 (`Number.MIN_VALUE`) から 21024 (`Number.MAX_VALUE`) までの正の浮動小数点数の値が格納できるようになっていますが、安全に格納できるのは -(253 − 1) (`Number.MIN_SAFE_INTEGER`) から 253 − 1 (`Number.MAX_SAFE_INTEGER`) の範囲です。この範囲を外れると、JavaScript は整数を安全に表現できなくなり、代わりに倍精度浮動小数点数の近似値で表現されます。数値が安全な整数の範囲内かどうかは `Number.isSafeInteger()` を用いて調べることができます。

±(2-1074 to 21024) の範囲を外れた値は、自動的に次のように変換されます。

* `Number.MAX_VALUE` より大きな正の数は `+Infinity` に変換されます。
* `Number.MIN_VALUE` より小さな正の数は `+0` に変換されます。
* -`Number.MAX_VALUE` より小さな負の数は `-Infinity` に変換されます。
* -`Number.MIN_VALUE` より大きな負の数は `-0` に変換されます。

`+Infinity` と `-Infinity` は数学的な無限大と同じような振る舞いをしますが、若干の違いがあります。詳しくは `Number.POSITIVE_INFINITY` と `Number.NEGATIVE_INFINITY` を参照してください．

数値型には、複数の表現を持つ値が1つだけあります。`0` は `-0` と `+0` の両方で表します（`0` は `+0` の別名です）。実際には、異なる表現にほとんど違いはありません。例えば、`+0 === -0` は `true` です。ただし、ゼロで割ったときには気づくことができるようになっています。

```js
console.log(42 / +0); // Infinity
console.log(42 / -0); // -Infinity
```

`NaN` ("**N**not **a** **N**umber") は、特殊な数値の一種で、演算操作の結果が数値として発生しない場合によく遭遇します。また、JavaScript で唯一、それ自身と等しくない値でもあります。

数値は概念的には「数学的な値」であり、常に暗黙のうちに浮動小数点`でエンコードされていますが、JavaScriptではビット演算子を提供しています。ビット演算子を運営する場合、最初の数値は 32 ビット整数に変換されます。

**メモ:** ビット演算子でビットマスクを使用すれば、 1 つの数値で複数の論理値を表現することも可能ですが、 JavaScript は（論理型の配列や名前付きプロパティに論理値が割り当てられたオブジェクトのような）論理値の集合を表現する手段を提供しているため、この行いは悪い習慣として見なされています。ビットマスクはコードの可読性、わかりやすさ、保守性を大きく損ないます。

ローカルストレージの制限に対処しようとするときや、極端な用途（ネットワーク上の各ビットがカウントされる場合など）のように、非常に制約された環境では、このような技術を使用する必要がある場合があります。この技術は、サイズを最適化するために導ける最後の手段である場合にのみ考えることができます。

### 長整数型

長整数型 (`BigInt`) は、任意の精度で整数を表現できる JavaScript の数値プリミティブです。長整数型を使えば、数値型で扱うことができる安全な整数の限界 (`Number.MAX_SAFE_INTEGER`) を超える、大きな整数を安全に格納して操作することができます。

長整数型は、整数の末尾に `n` を追加するか、 `BigInt()` 関数を呼び出すことで作成します。

この例は、`Number.MAX_SAFE_INTEGER` をインクリメントすると期待される結果が返ってくることを示しています。

```js
// 長整数型
const x = BigInt(Number.MAX\_SAFE\_INTEGER); // 9007199254740991n
x + 1n === x + 2n; // false。9007199254740992n と 9007199254740993n は等しくない

// 数値型
Number.MAX\_SAFE\_INTEGER + 1 === Number.MAX\_SAFE\_INTEGER + 2; // true。両方とも 9007199254740992
```

長整数型は、整数型と同じように `+`, `*`, `-`, `**`, `%` などの演算子を使用することができます。唯一使えないものは `>>>` です。長整数型は数値型と数学的な値が同じであっても厳密等価にはなりませんが、等価にはなります。

長整数値は、常に複数の値より正確であるわけでも、常に正確でないわけでもありません。長整数は小数値を表すことはできませんが、大きな整数をより正確に表すことができるからです。どちらの種類も他の種類を内包しておらず、相互に置換可能なものではありません。算術式で長整数値を通常の数値と混合した場合、または、暗黙的に変換しようとした場合、 `TypeError` が発生します。

### 文字列型

文字列型 (`String`) は、テキストデータを表し、UTF-16 コード単位 を表す 16 ビット符号なし整数値のシーケンスとしてエンコードさます。文字列の各要素は、文字列の中の位置を占めます。最初の要素は位置 `0` にあり、次の要素は位置 `1` にある、という具合になります。文字列の length は、その中の UTF-16 コード単位の個数で、実際の Unicode 文字数とは異なる場合があります。詳細は `String` を参照してください。

JavaScriptの文字列は不変です。つまり、一度文字列が作成されると、それを変更することはできません。文字列メソッドは、現在の文字列の内容に基づいて新しい文字列を作成します。例えば、次のような場面です。

* `substring()` を使用して元の文字列の部分文字列を作成する。
* 2 つの文字列を、連結演算子 (`+`) または `concat()` を用いて連結する。

#### 「文字列に型付けした」コードに注意！

複雑なデータを表現するために文字列を使用したい思うこともあるでしょう。これには短期的なメリットがあります。

* 結合することで、複合文字列を簡単に作成できます。
* 文字列はデバッグしやすいです（出力される情報は常に文字列に含まれているものです）。
* 文字列は多くの API（入力フィールド、ローカルストレージの値、 `XMLHttpRequest` の `responseText` を使用したレスポンス、など）において共通分母であり、文字列だけで作業したいという誘惑に駆られることがあります。

規則さえあれば、どのようなデータ構造でも文字列で表現することが可能ですが、これは良い考えとは言えません。例えば、区切り文字を使用することでリストを模倣することができますが（JavaScript の配列の方が適しています）、残念なことに区切り文字がリストの要素で使用されてしまった場合、リストが壊れてしまいます。エスケープした文字を使用することでこの問題に対処することは可能ですが、その規則をすべてに用意する必要がある上、不必要なメンテナンスの負担を生み出します。

文字列はテキストデータには向いていますが、複雑なデータを表す場合は文字列を解釈し、適切な抽象化を用いる必要があります。

### シンボル型

シンボル (`Symbol`) は**一意**で**不変**のプリミティブ値であり、オブジェクトのプロパティのキーとして使用することができます。一部のプログラミング言語では、「アトム」と呼ばれています。シンボルの目的は、他のコードのキーと衝突しないことが保証された固有のプロパティキーを作成することです。

オブジェクト
------

コンピューター科学において、オブジェクトは識別子によって参照可能なメモリー内の値です。JavaScript では、オブジェクトは唯一の変更可能な値です。関数は、実際には、*callable* という追加の機能を持つオブジェクトでもあります。

### プロパティ

JavaScript では、オブジェクトはプロパティの集合として見ることができます。オブジェクトリテラル構文は、初期化される限定された一連のプロパティです。その後でプロパティは追加したり削除したりすることができます。プロパティのキーは、文字列またはシンボルのどちらかです。プロパティの値は、他のオブジェクトを含め、どのような種類の値でもよいので、複雑なデータ構造を構築することが可能です。

オブジェクトプロパティには、データプロパティとアクセサープロパティの 2 種類があります。それぞれのプロパティには、対応する属性があります。それぞれの属性は、JavaScript エンジンが内部でアクセスしますが、`Object.defineProperty()` で設定したり、`Object.getOwnPropertyDescriptor()` で読み取ったりすることができます。様々なニュアンスについては、`Object.defineProperty()` のページで詳しく解説しています。

#### データプロパティ

データプロパティは、キーと値を関連付けます。以下の属性で記述することができます。

`value`

プロパティの get アクセスによって取得される値です。JavaScript の任意の値を指定することができます。

`writable`

プロパティに代入することで変更可能かどうかを示す論理値です。

`enumerable`

プロパティが `for...in` ループで列挙可能かどうかを示す論理値です。列挙可能性が他の機能や構文とどのようにやり取りするかについては、プロパティの列挙可能性と所有権も参照してください。

`configurable`

論理値で、プロパティの削除、アクセサプロパティへの変更、属性の変更が可能かどうかを示します。

#### アクセサープロパティ

キーを、値を取り出したり保存したりするための 1 つまたは 2 つのアクセサー関数 (`get` および `set`) と関連づけるものです。

**メモ:** アクセサー *プロパティ* であり、アクセサー *メソッド* ではないことを認識することが重要です。関数を値として用いることで、JavaScript オブジェクトにクラスのようなアクセサーを表すことができますが、それはオブジェクトをクラスにするわけではありません。

アクセサープロパティには、以下の属性があります。

`get`

空の引数リストで呼び出される関数で、値への取得アクセスが行われるたびに、プロパティ値を取得します。ゲッターも参照してください。`undefined` にすることができます。

`set`

割り当てる値を格納した引数で呼び出される関数です。指定したプロパティを変更しようとしたときに実行されます。セッターも参照してください。`undefined` にすることができます。

`enumerable`

プロパティが `for...in` ループで列挙可能かどうかを示す論理値です。列挙可能性が他の機能や構文とどのようにやり取りするかについては、プロパティの列挙可能性と所有権も参照してください。

`configurable`

論理値で、プロパティの削除、アクセサプロパティへの変更、属性の変更が可能かどうかを示します。

オブジェクトのプロトタイプは、他のオブジェクトや `null` を指しています。これは概念的にはオブジェクトの隠しプロパティで、一般的には `[[Prototype]]` として表されます。オブジェクトの `[[Prototype]]` のプロパティは、オブジェクト自身にもアクセスすることができます。

オブジェクトはアドホックなキーと値のペアであるため、マップとしてよく使用されます。しかし、人間工学、セキュリティ、パフォーマンスの課題がある場合があります。任意のデータを格納するためには、代わりに `Map` を使用してください。`Map` のリファレンスには、キーと値の関連性を格納するためのプレーンオブジェクトとマップの間のメリットとデメリットについてより詳しい議論が含まれています。

### 日付

日付を表現する場合は、JavaScript に組み込まれた `Date` ユーティリティを使用するのが最適です。

### 添字付きコレクション: 配列および型付き配列

配列は、整数値をキーにするプロパティと `length` プロパティの間に特殊な関係の存在する、標準オブジェクトです。

さらに、配列は `Array.prototype` を継承しており、配列を操作するための便利なメソッドを提供しています。例えば、 `indexOf()` (配列中の値の検索) や `push()` (配列への要素の追加) などです。これにより、配列はリストや集合を表現するのに最適な候補となります。

型付き配列は、基盤となるバイナリーデータバッファーの配列風のビューを表現し、配列と同様の意味づけを持つメソッドを数多く提供します。「型付き配列」は `Int8Array`、`Float32Array` などの様々なデータ構造の総称である。より詳しい情報は型付き配列のページを調べてください。型付き配列は、よく `ArrayBuffer` や `DataView` と併用して使用します。

### キー付きコレクション: Map, Set, WeakMap, WeakSet

これらのデータ構造は、オブジェクトへの参照をキーとして扱います。`Set` と `WeakSet` はオブジェクトの集合を表し、`Map` と `WeakMap` はオブジェクトに値を関連付けます。

自分で `Map` や `Set` を実装することもできます。しかし、オブジェクトは（例えば `<` "less than" の意味で）比較することができず、エンジンもオブジェクトのハッシュ関数を公開していないので、検索性能は必然的に線形になります。これらのネイティブ実装（`WeakMap` を含む）の検索性能は、一定時間に対してほぼ対数となります。

通常、DOM ノードにデータをバインドするには、オブジェクトに直接プロパティを設定するか、 `data-*` 属性を使用します。これらの手法は同じコンテクストで実行されるあらゆるスクリプトからデータの利用が可能であるため、不都合な面を持ち合わせていました。 `Map` や `WeakMap` を使うと、オブジェクトへの*プライベート*なデータバインドを簡単に行うことができます。

`WeakMap` と `WeakSet` はオブジェクトのキーしか使用できず、キーが集合に残っていてもガベージコレクションの対象とすることができます。これらは、メモリー使用の最適化に固有の仕様として使用します。

### 構造化データ: JSON

JSON (**J**ava**S**cript **O**bject **N**otation) は JavaScript から派生した、汎用データ構造をもつ軽量なデータ交換形式であり、多くのプログラミング言語で使用されています。JSON は、異なる環境や言語間でも移行可能な普遍的なデータ構造を構築します。詳しくは `JSON` を参照してください。

### 標準ライブラリーに含まれる他のオブジェクト

JavaScript には組み込みオブジェクトの標準ライブラリーがあります。オブジェクトの詳細については、リファレンスを参照してください。

型変換
---

前述のように、JavaScript は弱い型付け言語です。つまり、ある型の値を使用する際に、別の型が期待される場合でも、言語が適切な型に変換してくれる場合が多いのです。そのために、JavaScript では、いくつかの変換ルールを定義しています。

### プリミティブ変換

プリミティブ変換処理は、プリミティブ値が期待されるものの、実際の入力する種類に強い希望がない場合に使用します。文字列、数値、長整数が同じように受け入れられる場合がほとんどです。例を示します。

* `Date()` コンストラクターは、`Date` インスタンスでない引数を 1 つ受け取ります。文字列は日付文字列を表し、数値はタイムスタンプを表します。
* `+` 演算子は、一方のオペランドが文字列の場合、文字列の連結を行い、それ以外の場合は数値の加算を行います。
* `==` 演算子は、オペランドの一方がプリミティブで、もう一方がオブジェクトの場合、オブジェクトは入力する種類が決まっていないプリミティブ値に変換されます。

この操作は、値が既にプリミティブである場合、変換を行いません。オブジェクトは、その `[@@toPrimitive]()` （`"default"` をヒントとして）、`valueOf()`、`toString()` の順にメソッドが呼び出されてプリミティブに変換されます。プリミティブ変換では `toString()` の前に `valueOf()` が呼び出されますが、これは数値変換の動作と同様であり、文字列変換とは異なっていることに注意ください。

`[@@toPrimitive]()` メソッドは、存在する場合、プリミティブを返す必要があります。オブジェクトを返すと `TypeError` になります。`valueOf()` と `toString()` については、一方がオブジェクトを返す場合、その返値は無視され、代わりにもう一方の返値が使用されます。どちらも存在しなかった場合、またはどちらもプリミティブ値を返さなかった場合は `TypeError` が発生します。例として、以下のコードで説明します。

```js
console.log({} + []); // "[object Object]"
```

`{}` にも `[]` にも `[@@toPrimitive]()` メソッドはありません。`{}` と `[]` は両方とも `valueOf()` を `Object.prototype.valueOf` から継承しており、これはオブジェクト自体を返します。返値がオブジェクトなので、これは無視されます。従って、代わりに `toString()` が呼び出されます。 `{}.toString()` は `"[object Object]"` を返し、一方 `[].toString()` っは `""` を返すので、結果はこれらを結合した `"[object Object]"` となります。

プリミティブ型に変換する場合は、常に `[@@toPrimitive]()` メソッドが優先されます。プリミティブ型の変換は、一般に `valueOf()` が優先的に呼び出されるため、数値の変換と同じように振る舞います。しかし、独自の `[@@toPrimitive]()` メソッドを持つオブジェクトは、任意のプリミティブ型を返すことができます。組み込みオブジェクトの中で、`Date` と `Symbol` オブジェクトのみが、`[@@toPrimitive]()` メソッドを上書きします。`Date.prototype[@@toPrimitive]()` は `"default"` ヒントを `"string"` であるかのように扱い、`Symbol. prototype[@@toPrimitive]()` はヒントを無視し、常にシンボルを返します。

### 数値変換

数値の型には数値型と長整数型の 2 種類があります。言語が数値か長整数かを具体的に指定する場合もあります（`Array.prototype.slice()` は添字が数値でなければならないなど）。他にも、どちらかを許容し、オペランドの種類によって異なる処理を行う場合もあります。他にも暗黙の変換を許さない厳密な強制処理については、数値型への変換や長整数型への変換を参照して下さい。

数値変換は、数値変換とほぼ同じですが、長整数の場合はは `TypeError` を発生させずにそのまま返す点が異なります。すべての算術演算子は、数値型と長整数型の両方がオーバーロードされているため、数値変換が行わわれます。唯一の例外は単項プラスで、これは常に数値型への変換を行います。

### その他の変換

すべてのデータ型には、Null、Undefined、シンボルを除き、それぞれの変換処理があります。詳しくは、文字列への変換、論理型への変換、オブジェクトへの変換を参照してください。

お気づきかもしれませんが、オブジェクトをプリミティブに変換する経路は 3 つあります。

* プリミティブ変換: `[@@toPrimitive]("default")` → `valueOf()` → `toString()`
* 数値変換、数値型への変換、長整数型への変換: `[@@toPrimitive]("number")` → `valueOf()` → `toString()`
* 文字列変換: `[@@toPrimitive]("string")` → `toString()` → `valueOf()`

すべての場合において、`[@@toPrimitive]()` が存在する場合は、呼び出し可能でプリミティブを返す必要があり、`valueOf` や `toString` が呼び出し可能でないかオブジェクトを返さない場合は無視されます。この処理の終わりには、成功すれば結果がプリミティブであることが保証されます。結果として得られるプリミティブは、コンテキストに応じてさらなる変換が行われることがあります。

関連情報
----

* JavaScript Data Structures and Algorithms by Oleksii Trekhleb
* Nicholas Zakas による、JavaScript における一般的なデータ構造とアルゴリズムのまとめ
* Search Tre(i)es implemented in JavaScript
* Data Types and Values in the ECMAScript specification


並行モデルとイベントループ - JavaScript

並行モデルとイベントループ
=============

JavaScript は、"event loop"に基づく同時実行モデルを持ちます。このモデルは C 言語や Java のような他の言語のモデルとかなり異なっています。

ランタイムの概要
--------

後続のセクションでは、理論モデルを説明します。現代の JavaScript エンジンは、記載されたセマンティクスに従って実装され、また高度に最適化されています。

### 視覚表示

![Stack, heap, queue](/ja/docs/Web/JavaScript/Event_loop/the_javascript_runtime_environment_example.svg)

### スタック

関数呼び出しはフレームのスタックを形成します。

```js
function foo(b) {
  var a = 10;
  return a + b + 11;
}

function bar(x) {
  var y = 3;
  return foo(x \* y);
}

console.log(bar(7)); // returns 42
```

`bar`を呼び出すと、`bar`の引数とローカル変数を含んだ最初のフレームが生成されます。`bar`が`foo`を呼び出すと、`foo`の引数とローカル変数を含んだ２番目のフレームが生成され、最初のフレームの上にプッシュされます。`foo`から返ると、先頭のフレーム要素はスタックからポップされます(`bar`のコールフレームのみが残ります)。`bar`から返るときスタックは空になります。

### ヒープ

オブジェクトはヒープに割り当てられています。ヒープは、メモリの大規模で大部分は構造化されていない領域を意味する名前です。

### キュー

JavaScript ランタイムはメッセージキューを含んでいます。メッセージキューは、処理されるメッセージのリストです。各メッセージに関数を関連付けられています。スタックが空のとき、メッセージがキューから取り出され、処理されます。その処理は、関連する関数の呼び出し(と最初のスタックフレームの作成)で構成されています。スタックが再度空になると、メッセージ処理は終了します。

イベントループ
-------

`event loop`という名前は、それが一般的にどのように実装されたかに従って付けられました。これは通常、次のものに似ています。

```js
while (queue.waitForMessage()) {
  queue.processNextMessage();
}
```

`queue.waitForMessage`はもしその時点でメッセージが存在しないのであれば、同期的にメッセージが到着するのを待ちます。

### "Run-to-completion"

その他のメッセージが処理される前に、各メッセージは完全に処理されています。 関数が実行されるたびに、それが横取りすることはできず、他のコードが実行される前に、完全に実行されます（および関数が操作するデータを変更することができる）という事実を含め、プログラムについて推論するときにいくつかの素晴らしい特性を提供しています。例えば、これは C とは異なります。というのは、関数はスレッドで実行されている場合、それは別のスレッドでいくつかの他のコードを実行するには、任意の時点で停止することができます。

このモデルの欠点は、メッセージが完了するまでに時間がかかりすぎる場合は、Web アプリケーションはクリックやスクロールのようなユーザインタラクションを処理することができないことです。ブラウザは"スクリプトは実行に非常に時間がかかる"ダイアログを用いてこれを軽減します。追従するお勧めは、メッセージを短い処理にし、可能な場合には、いくつかのメッセージに一つのメッセージを切り縮めることです。

### メッセージの追加

Web ブラウザでは、メッセージは、イベントが発生し、それに接続されているイベントリスナーがある任意の時間に追加されます。イベントリスナーがない場合、イベントは失われます。だから、他のイベントと同様に、クリックイベントハンドラを持つ要素をクリックすると、メッセージが追加されます。

`setTimeout` (en-US)を呼び出すと、2 番目の引数として渡された時間が経過した後、メッセージがキューに追加されます。キューに他のメッセージがない場合、メッセージはすぐに処理されます。しかしながら、メッセージがある場合、`setTimeout`メッセージは他のメッセージを処理するために待機する必要があります。そのため第二引数は、保証時間ではなく、最小の時間を示しています。

### 一緒に通信するいくつかのランタイム

ウェブワーカーやクロスオリジンの iframe は、独自のスタック、ヒープ、およびメッセージキューがあります。二つの異なるランタイムのみ`postMessage`メソッドによって送信メッセージを介して通信することができます。他のランタイムが`message`イベントをリッスンする場合、このメソッドは他のランタイムにメッセージを追加します。

ブロッキング不可
--------

イベントループモデルの非常に興味深い特性は、他の多くの言語とは異なり、JavaScript は決してブロックしないことです。I/O の取り扱いは、通常、イベントとコールバックを介して行われます。そのため、アプリケーションはIndexedDBのクエリやXHRリクエストが返るのを待っている時も、ユーザ入力のような他のことを処理することができます。

`alert`か同期 XHR のようにレガシーな例外が存在しますが、それらを避けることは良い慣習とされています。例外に対する例外は存在することに気をつけてください(と言っても、たいていは他の何かというよりはむしろ実装のバグですが)。


等価性の比較と同一性 - JavaScript

等価性の比較と同一性
==========

JavaScript には、異なる値の比較演算が 3 つあります。

* `===` — 厳密な等価性（三重等号）
* `==` — 緩い等価性（二重等号）
* `Object.is()`

どの演算子を使用するかは、どのような比較を行いたいかに依存します。簡単に言えば、

* 二重等号 (`==`) は 2 つのものを比較する前に型変換を実行し、`NaN`, `-0`, `+0` を IEEE 754 に準拠して特別扱いします（よって `NaN != NaN`, `-0 == +0` になります）。
* 三重等号 (`===`) は二重等号と同じ比較を（`NaN`, `-0`, `+0` の特別扱いを含めて）しますが、型変換を行いません。もし型が異なれば、`false` が返されます。
* `Object.is()` は型変換を行わず、`NaN`, `-0`, `+0` の特別扱いもしません（これらの特殊な数値を除いて `===` と同じ動作をします）。

これらは、JavaScript における 4 つの等価性アルゴリズムのうち、3 つに対応します。

* IsLooselyEqual: `==`
* IsStrictlyEqual: `===`
* SameValue: `Object.is()`
* SameValueZero: 多くの組み込み操作で使用される

なお、これらの区別はプリミティブ値の扱いについてのことです。いずれの場合も引数が概念的に似た構造を持つかどうかを比較する訳ではありません。プリミティブ値ではない `x` および `y` オブジェクトが同一の構造を持っていてもオブジェクトが異なれば、上記のいずれの形でも `false` と評価されます。

=== による厳密な等価性
-------------

厳密な等価性は、2 つの値が等しいか比較します。比較対象の値はどちらも、比較する前に別の値へ暗黙のうちに変換されることはありません。値が異なる型の場合、それらの値は等しくないとみなします。値が同じ型で数値ではない場合、同じ値であれば等しいとみなします。最後に、どちらの値も数値である場合、どちらも `NaN` ではなく同じ値である、あるいは一方が `+0` かつもう一方が `-0` であるときに等しいとみなします。

```js
const num = 0;
const obj = new String("0");
const str = "0";

console.log(num === num); // true
console.log(obj === obj); // true
console.log(str === str); // true

console.log(num === obj); // false
console.log(num === str); // false
console.log(obj === str); // false
console.log(null === undefined); // false
console.log(obj === null); // false
console.log(obj === undefined); // false
```

厳密な等価性は、たいていの使い方で正しい比較になります。数値以外のあらゆる値において、これは「値が自分自身と等しい」という明快な解釈を用います。数値においては、２つの極めて特殊なケースを扱えるようにわずかに異なる解釈を用います。１つ目は浮動小数点数の 0 には正と負の符号付きが存在することです。これは、ある種の数学的な解を表すために役立ちますが、ほとんどの場合は `+0` と `-0` の違いを意識せず、厳密な等価性ではこれらを同じ値として扱います。２つ目は浮動小数点数には非数 `NaN` の概念があることです、これは例えば正の無限大に負の無限大を加算するといった一定の数学的に明確ではない問題達の解を表します。厳密な等価性では `NaN` を他のどの値 (自分自身も含む) とも等しくないものとして扱います。（`(x !== x)` が `true` になる唯一の場合は `x` が `NaN` である場合です。）

また、`===` 以外にも厳密な等価性は配列のインデックスを探すメソッドで使用されます。`Array.prototype.indexOf()`、`Array.prototype.lastIndexOf()`、`TypedArray.prototype.indexOf()`、`TypedArray.prototype.lastIndexOf()`、`case` での比較です。つまり、`indexOf(NaN)` を使用して配列の `NaN` 値のインデックスを探したり、`NaN` を `switch` 文の `case` 値として使用して何かと一致させることはできません。

```js
console.log([NaN].indexOf(NaN)); // -1
switch (NaN) {
  case NaN:
    console.log("Surprise"); // 何も出力されない
}
```

== による緩い等価性
-----------

緩い等価性は対称的です。（変換の順序を除いて） `A` と `B` の値が何であっても、`A == B` は常に `B == A` と同じ意味づけになります。`==` を使用して緩い等価性を実行する場合の動作は以下の通りです。

1. オペランドが同じ型である場合、以下のように比較されます。
    * オブジェクト: 両方のオペランドが同じオブジェクトを参照している場合に限り `true` を返します。
    * 文字列: 両方のオペランドに同じ文字が同じ順番で入っている場合のみ `true` を返します。
    * 数値: 両方のオペランドが同じ値である場合のみ `true` を返します。`+0` と `-0` は同じ値として扱われます。どちらかのオペランドが `NaN` である場合は `false` を返します。従って、`NaN` は `NaN` とは等しくなりません。
    * 論理値: 両方が `true` または両方が `false` の場合のみ `true` を返します。
    * 長整数: 両方のオペランドが同じ値である場合のみ `true` を返します。
    * シンボル: 両方のオペランドが同じシンボルを参照している場合のみ `true` を返します。
2. オペランドの一方が `null` または `undefined` である場合、もう一方も `null` または `undefined` であれば `true` を返します。それ以外の場合は `false` を返す。
3. オペランドの一方がオブジェクトで、もう一方がプリミティブである場合、オブジェクトをプリミティブに変換します。
4. この段階で、両方のオペランドはプリミティブ（文字列、数値、論理値、シンボル、長整数のいずれか）に変換されます。残りの変換は、ケースバイケースで行われます。
    * 両方が同じ型であれば、手順 1 を使用して比較します。
    * 一方のオペランドがシンボルで、もう一方がそうでない場合は、`false` を返します。
    * 一方のオペランドが論理型で、もう一方がそうでない場合は、論理値から数値への変換が行われます。`true` は 1 に変換され、`false` は 0 に変換されます。それから 2 つのオペランドを緩い等価性で比較します。
    * 数値から文字列へ: 文字列から数値へ変換します。変換に失敗すると `NaN` となり、等価性が `false` となることが保証されています。
    * 数値から長整数へ: 数値で比較します。数値が ±Infinity または `NaN` であれば、`false` を返します。
    * 文字列から長整数へ: 文字列を `BigInt()` コンストラクターと同じアルゴリズムを使用して長整数へと変換します。変換に失敗した場合は、 `false` を返します。

伝統的に、そして ECMAScript によれば、すべてのプリミティブとオブジェクトは `undefined` および `null` と緩い不等価であるとされています。しかし、ほとんどのブラウザーは、あるコンテキストにおいて、非常に狭いクラスのオブジェクト（具体的には、あらゆるページの `document.all` オブジェクト）が、あたかも `undefined` という値を持つかのように振る舞うことを許可しています。緩い等価性はそのようなコンテキストの 1 つです。`null == A` と `undefined == A` は、A が `undefined` をエミュレートするオブジェクトである場合にのみ真と評価されます。他に、オブジェクトが `undefined` や `null` と緩やかな等価性を持つことはありません。

ほとんどの場合、緩い等価性を使用することは推奨されません。厳密な等価性を用いた比較の結果は予測しやすく、型変換がないため、よりすばやく評価できる可能性があります。

次の例は、数値プリミティブ `0`、長整数プリミティブ `0n`、文字列プリミティブ `'0'`、`toString()` の値が `'0'` であるオブジェクトを含む緩い等価性の比較を示しています。

```js
const num = 0;
const big = 0n;
const str = "0";
const obj = new String("0");

console.log(num == str); // true
console.log(big == num); // true
console.log(str == big); // true

console.log(num == obj); // true
console.log(big == obj); // true
console.log(str == obj); // true
```

緩い等価性は、 `==` 演算子でのみ使用されます。

Object.is() を使用した同値等価性
----------------------

同値等価性は、すべての状況で 2 つの値が*機能的に同一か*を判断します（この用法はリスコフの置換原則の実践例と言えます）。例として、変更不可のプロパティを変化させようとした場合を見てみましょう。

```js
// Number コンストラクターに変更不可の NEGATIVE\_ZERO プロパティを追加
Object.defineProperty(Number, "NEGATIVE\_ZERO", {
  value: -0,
  writable: false,
  configurable: false,
  enumerable: false,
});

function attemptMutation(v) {
  Object.defineProperty(Number, "NEGATIVE\_ZERO", { value: v });
}
```

変更不可のプロパティを変更しようとしたとき、`Object.defineProperty` は例外を発生させますが、実際の変更が要求されなかった場合は何もしません。`v` が `-0` であれば、変更が要求されていないので、エラーは発生しません。内部的には、変更不可のプロパティが再定義された場合、新たに指定された値と現在の値が同値等価性によって比較されます。

同値等価性は `Object.is` メソッドによって提供されます。等価な同一性を持つ値が期待される場合、言語上のほとんどの場所で使用されます。

同値ゼロ等価性
-------

同値等価性に似ていますが、+0 と -0 は等しいとみなします。

同値ゼロ等価性 (Same-value-zero equality) は JavaScript API として公開されていませんが、独自のコードで実装することができます。

```js
function sameValueZero(x, y) {
  if (typeof x === "number" && typeof y === "number") {
    // x と y は等しい（-0 と 0 の場合も含む)か、両方が NaN である
    return x === y || (x !== x && y !== y);
  }
  return x === y;
}
```

同値ゼロは `NaN` を等価に扱うという点で厳密な等価性と異なり、`-0` を `0` と等価に扱うことのみが同値等価性と異なります。これは特に `NaN` を扱う場合、検索時に最も賢明な動作をするようになります。`Array.prototype.includes()`、`TypedArray.prototype.includes()`、またキーの等価性を比較するために `Map` や `Set` のメソッドで使用されます。

等価性の方法の比較
---------

二重等号と三重等号を比較するとき、一方は他方を「拡張した」ものである言われがちです。例えば、二重等号は三重等号と同じことをすべて行うだけでなくオペランドの型変換も行うことから、三重等号を拡張したものであると聞いたことがあるかもしれません。例えば、`6 == "6"` となります。あるいは二重等号が基本形であり、三重等号は 2 つのオペランドが同一の型であることを要求するという制約を加えていることから、三重等号が拡張形であると言われたかもしれません。

しかし、この考え方は、等価性の比較が一次元の「スペクトル」を形成し、「完全に厳密な」ものが一端にあり、「完全に緩い」ものが他にもあることを意味します。このモデルは`Object.is`では不十分で、二重等号よりも「緩い」、三重等号よりも「厳しい」ものではなく、その中間（つまり、二重等号よりも厳しく、三重等号よりも緩い）にも当てはまらないからです。同一性を比較した以下の表から、`Object.is` が `NaN` を扱う方法が原因であることがわかります。`Object.is(NaN, NaN)` が `false` に評価されるのであれば、`-0` と `+0` を区別することにより、三重等号より厳密であることから緩い/厳密のスペクトルに*含めることができる*ことに注目してください。しかし `NaN` の扱いは、これが正しくないことを表します。残念ながら、`Object.is` は等価演算子に関する緩さや厳密さではなく、単純に固有の特性の観点から考えなければなりません。

| x | y | `==` | `===` | `Object.is` | 同値ゼロ |
| --- | --- | --- | --- | --- | --- |
| `undefined` | `undefined` | `✅ true` | `✅ true` | `✅ true` | `✅ true` |
| `null` | `null` | `✅ true` | `✅ true` | `✅ true` | `✅ true` |
| `true` | `true` | `✅ true` | `✅ true` | `✅ true` | `✅ true` |
| `false` | `false` | `✅ true` | `✅ true` | `✅ true` | `✅ true` |
| `'foo'` | `'foo'` | `✅ true` | `✅ true` | `✅ true` | `✅ true` |
| `0` | `0` | `✅ true` | `✅ true` | `✅ true` | `✅ true` |
| `+0` | `-0` | `✅ true` | `✅ true` | `❌ false` | `✅ true` |
| `+0` | `0` | `✅ true` | `✅ true` | `✅ true` | `✅ true` |
| `-0` | `0` | `✅ true` | `✅ true` | `❌ false` | `✅ true` |
| `0n` | `-0n` | `✅ true` | `✅ true` | `✅ true` | `✅ true` |
| `0` | `false` | `✅ true` | `❌ false` | `❌ false` | `❌ false` |
| `""` | `false` | `✅ true` | `❌ false` | `❌ false` | `❌ false` |
| `""` | `0` | `✅ true` | `❌ false` | `❌ false` | `❌ false` |
| `'0'` | `0` | `✅ true` | `❌ false` | `❌ false` | `❌ false` |
| `'17'` | `17` | `✅ true` | `❌ false` | `❌ false` | `❌ false` |
| `[1, 2]` | `'1,2'` | `✅ true` | `❌ false` | `❌ false` | `❌ false` |
| `new String('foo')` | `'foo'` | `✅ true` | `❌ false` | `❌ false` | `❌ false` |
| `null` | `undefined` | `✅ true` | `❌ false` | `❌ false` | `❌ false` |
| `null` | `false` | `❌ false` | `❌ false` | `❌ false` | `❌ false` |
| `undefined` | `false` | `❌ false` | `❌ false` | `❌ false` | `❌ false` |
| `{ foo: 'bar' }` | `{ foo: 'bar' }` | `❌ false` | `❌ false` | `❌ false` | `❌ false` |
| `new String('foo')` | `new String('foo')` | `❌ false` | `❌ false` | `❌ false` | `❌ false` |
| `0` | `null` | `❌ false` | `❌ false` | `❌ false` | `❌ false` |
| `0` | `NaN` | `❌ false` | `❌ false` | `❌ false` | `❌ false` |
| `'foo'` | `NaN` | `❌ false` | `❌ false` | `❌ false` | `❌ false` |
| `NaN` | `NaN` | `❌ false` | `❌ false` | `✅ true` | `✅ true` |

### Object.is() と三重等号の使いどころ

一般的に、`Object.is` のゼロに対する特別な動作が関心の対象になりえると思われるのは、ある種のメタプログラミング方式に則る時、特にプロパティ記述子に関して `Object.defineProperty` の特徴の一部を再現したい時に限られます。このような要件が必要ないのであれば、`Object.is` ではなく `===` を使用してください。2 つの `NaN` 値を比較した結果が `true` になることが必要な場合であっても、通常は、`NaN` をチェックして特別扱いする方が (前バージョンの ECMAScript からは `isNaN` メソッドを使えます) 、比較処理中に現れた全てのゼロについてその符号が周囲の処理からどう影響されるのか悩むよりも簡単です。

すべてを網羅してはいませんが、`-0` と `+0` の区別が発生する可能性がある内蔵メソッドや演算子を以下に示します。コード中ではこれらを考慮して下さい:

`- (単項否定演算子)`

以下例を考えてみてください。

```js
const stoppingForce = obj.mass \* -obj.velocity;
```

`obj.velocity` が `0` である (あるいは計算結果が `0` になる) とき、そこで `-0` が生成されて `stoppingForce` に伝播します。

`Math.atan2`, `Math.ceil`, `Math.pow`, `Math.round`

引数に `-0` が存在しなくても、場合によってはこれらのメソッドの戻り値として `-0` が式に取り込まれる可能性があります。例えば、負の値の累乗で `-Infinity` が発生するように `Math.pow` を使用したとき、奇数の指数は `-0` に評価されます。それぞれのメソッドのドキュメントを確認してください。

`Math.floor`, `Math.max`, `Math.min`, `Math.sin`, `Math.sqrt`, `Math.tan`

引数のひとつが `-0` である場合に、これらのメソッドから `-0` を戻り値として得る可能性があります。例えば、`Math.min(-0, +0)` は `-0` になります。それぞれのメソッドのドキュメントを確認してください。

`~`, `<<`, `>>`

これらの演算子は、内部で ToInt32 アルゴリズムを使用します。内部の 32 ビット整数型は 0 の表現が 1 種類しかないため、逆の演算を行った後に `-0` は戻らないでしょう。例えば `Object.is(~~(-0), -0)` や `Object.is(-0 << 2 >> 2, -0)` は、`false` になります。

ゼロの符号を考慮していない場合に、`Object.is` に頼ることは危険でしょう。もちろん `-0` と `+0` を区別する意図があれば、これはまさに望むことです。

### 注意: Object.is() と NaN

`Object.is` の仕様書では、すべての `NaN` のインスタンスを同じオブジェクトとして扱っています。しかし、型付き配列が利用でき、インスタンスを区別することができるので、次の例のようにすべてのコンテキストで同じ動作をするとは限りません。

```js
const f2b = (x) => new Uint8Array(new Float64Array([x]).buffer);
const b2f = (x) => new Float64Array(x.buffer)[0];
// NaN のバイト表現を取得
const n = f2b(NaN);
// 最初のビットを変更する。これは符号ビットであり、NaN には関係ない
n[0] = 1;
const nan2 = b2f(n);
console.log(nan2); // NaN
console.log(Object.is(nan2, NaN)); // true
console.log(f2b(NaN)); // Uint8Array(8) [0, 0, 0, 0, 0, 0, 248, 127]
console.log(f2b(nan2)); // Uint8Array(8) [1, 0, 0, 0, 0, 0, 248, 127]
```

関連情報
----

* JS Comparison Table