9
// Case A
function Constructor() {
  this.foo = function() {
    ...
  };
  ...
}

// vs 
// Case B
function Constructor() {
  ...
};

Constructor.prototype.foo = function() {
  ...
}

人々がプロトタイプの使用を勧める主な理由の 1 つは、.fooプロトタイプの場合は 1 回作成されるのthis.fooに対し、他のアプローチを使用する場合は複数回作成されることです。

ただし、インタープリターがこれを最適化できることが期待されます。fooそのため、ケース Aでは関数のコピーが 1 つだけになります。

もちろん、クロージャーのために各オブジェクトに一意のスコープ コンテキストが引き続き存在しますが、各オブジェクトの新しい関数よりもオーバーヘッドが少なくなります。

最新の JS インタープリターはケース A を最適化して、関数のコピーが 1 つだけになるようにしますfooか?

4

3 に答える 3

10

はい、関数を作成すると、より多くのメモリが使用されます。

...そして、いいえ、インタープリターはケース A を単一の関数に最適化することはありません。

その理由は、JS スコープ チェーンでは、関数の各インスタンスが作成時に使用可能な変数を取得する必要があるためです。そうは言っても、現代のインタープリターは以前よりもケース A について優れていますが、主にクロージャー関数のパフォーマンスが数年前に既知の問題だったためです。

Mozilla は、この理由から不必要なクロージャを避けるように言っていますが、クロージャは JS 開発者のツールキットで最も強力で頻繁に使用されるツールの 1 つです。

更新: node.js (V8、Chrome の JS インタープリター) を使用して、コンストラクターの 1M の「インスタンス」を作成するこのテストを 実行しました。caseA = true私はこのメモリ使用量を取得します:

{
    rss: 212291584,       //212 MB
    vsize: 3279040512,    //3279 MB
    heapTotal: 203424416, //203 MB
    heapUsed: 180715856   //180 MB
}

そして、caseA = false私はこのメモリ使用量を取得します:

{
    rss: 73535488,       //73 MB
    vsize: 3149352960,   //3149 MB
    heapTotal: 74908960, //74 MB
    heapUsed: 56308008   //56 MB
}

したがって、クロージャー関数は間違いなく、ほぼ 3 倍の大幅に多くのメモリを消費しています。しかし、絶対的な意味では、インスタンスごとに最大 140 ~ 150 バイトの違いについて話しているだけです。(ただし、関数が作成されたときにスコープ内の変数の数に応じて増加する可能性があります)。

于 2011-09-16T23:26:46.927 に答える
2

ノードでいくつかの簡単なテストを行った後、ケース A と B の両方で、関数の実際のコードのコピーがfooメモリ内に 1 つだけあると思います。

ケース A - 関数コードへの参照を格納する実行ごとに作成される関数オブジェクトConstructor()と、その現在の実行スコープがあります。

ケース B - スコープが 1 つ、関数オブジェクトが 1 つしかなく、プロトタイプを介して共有されている。

于 2011-09-16T23:40:35.653 に答える
0

JavaScript インタープリターもプロトタイプ オブジェクトを最適化していません。タイプごとに 1 つしかない場合 (複数のインスタンスが参照する場合) にすぎません。一方、コンストラクターは、新しいインスタンスとその中で定義されたメソッドを作成します。したがって、定義上、これは実際にはインタープリターの「最適化」の問題ではなく、単に何が起こっているのかを理解することの問題です。

余談ですが、インタープリターがインスタンスメソッドを統合しようとすると、特定のインスタンスで値を変更することにした場合に問題が発生します (言語に頭痛の種が追加されないことをお勧めします) :)

于 2011-09-16T23:37:04.190 に答える