web-dev-qa-db-ja.com

JavaScript配列は物理メモリでどのように表現されますか?

混合データをJavaScript配列に格納したり、配列内の要素を他のタイプに変更したりできることは私の理解です。インタプリタはどのようにして物理メモリのどの要素にあるのかを追跡しますか?また、要素をより大きなデータ型に変更した場合、次の要素のデータの上書きはどのように防止されますか?.

配列には実際のオブジェクトへの参照のみが格納され、プリミティブは配列に配置されたときに裏でラップされると思います。

これが事実であると仮定すると、プリミティブ変数に別のハンドルがあり、配列に格納されている値を変更すると、同期性が維持されますか?

私はおそらく自分の質問にすでに回答していると思いますが、確かなことはわからず、問題に関する情報を見つけることができません。

49
user3056052

通常、配列は固定長の連続したメモリブロックを割り当てます。ただし、Javascriptでは、配列は特別なコンストラクターとアクセサーメソッドを持つオブジェクト型です。

つまり、次のようなステートメントです。

var arr = new Array(100000);

メモリを割り当てません!実際、配列のlengthプロパティの値を設定するだけです。配列を作成する場合、サイズが自動的に大きくなるため、サイズを宣言する必要はありません。したがって、代わりにこれを使用する必要があります。

var arr = [];

JavaScriptの配列はスパースです。つまり、配列のすべての要素にデータが含まれているとは限りません。つまり、実際にデータを含む要素のみが配列に存在します。これにより、アレイで使用されるメモリの量が削減されます。値はオフセットではなくキーによって配置されます。これらは単に便利な方法であり、複雑な数値分析に使用することを意図していません。

JavaScriptの配列は型指定されていないため、要素の値はオブジェクト、文字列、数値、ブール、関数、または配列にすることができます。配列とオブジェクトの主な違いは、配列内の最大の整数キーより大きい値を持つ長さプロパティです。

例:

空の配列を作成して、インデックス0とインデックス99に2つの要素を追加できます。長さは100ですが、配列内の要素の数は2です。

var arr = [];
arr[0] = 0;
arr[99] = {name: "John"};
console.log(arr.length); // prints 100
arr; // prints something like [0, undefined × 98, Object { name: "John"}]

質問に直接回答するには:

Q.混合データをJavaScript配列に格納したり、配列内の要素を他のタイプに変更したりできることは私の理解です。インタプリタはどのようにして物理メモリのどの要素にある要素を追跡するのですか?また、要素をより大きなデータ型に変更した場合、次の要素のデータの上書きはどのように防止されますか?

A.上記の私のコメントを読んだことがあれば、おそらくこれを知っているでしょう。 JavaScriptでは、配列はHashtable Object型であるため、インタプリタは物理メモリを追跡する必要がなく、要素の値を変更しても、連続したメモリブロックに格納されていないため、他の要素に影響を与えません。

-

Q.配列は実際のオブジェクトへの参照のみを格納し、プリミティブは配列に配置されたときに裏でラップされると思います。これが事実であると仮定すると、プリミティブ変数に別のハンドルがあり、配列に格納されている値を変更すると、同期性が維持されますか?

A.いいえ、プリミティブはラップされません。配列に割り当てられたプリミティブを変更しても、値によって格納されるため、配列の値は変更されません。一方、オブジェクトは参照によって格納されるため、オブジェクトの値を変更すると、その配列の変更が反映されます。

これがあなたが試すことができる例です:

var arr = [];
var obj = { name: "John" };
var isBool = true;

arr.Push(obj);
arr[1] = isBool;

console.log(arr[0]); // print obj.name
console.log(arr[1]); // print true

obj.age = 40;        // add age to obj
isBool = false;      // change value for isBool

console.log(arr[0]); // value here will contain age
console.log(arr[1]); // value here will still be true

また、次の2つの方法で配列を初期化すると、動作が異なることに注意してください。

var arr = new Array(100);
console.log(arr.length);        // prints 100
console.log(arr);               // prints []

var arr2 = new Array(100, 200);
console.log(arr2.length);       // prints 2
console.log(arr2);              // prints [100, 200]

JavaScript配列をメモリの連続したブロックとして使用する場合は、 TypedArray の使用を検討する必要があります。 TypedArrayを使用すると、メモリブロックをバイト配列として割り当て、生のバイナリデータにさらに効率的にアクセスできます。

ECMA-262 spec(ver 5.1) を読むことで、JavaScriptの複雑さについてさらに学ぶことができます。

45
Nick

ここにいくつかの考えの食べ物があります。一部のJavaScriptエンジンが実装する 単純な配列最適化をテストするためのjsperf を作成しました。

テストケースでは、それぞれ100万個の要素を持つ2つの配列を作成します。 a配列には数値のみが含まれます。 b配列には、オブジェクトである最初の要素を除いて同じ番号が含まれています。

var a = [ 0 ], b = [ { valueOf: function() { return 0; } } ];
for( var i = 1;  i < 1000000;  ++i ) {
    a[i] = b[i] = i;
}

valueOf配列の最初の要素にあるオブジェクトのbプロパティは0を返すため、演算は最初の配列と同じになります。

次に、2つのテストは2つの配列のすべての値を単純に合計します。

高速配列:

var x = 0;
for( var i = 0;  i < 1000000;  ++i ) {
    x += a[i];
}

遅い配列:

var x = 0;
for( var i = 0;  i < 1000000;  ++i ) {
    x += b[i];
}

Jsperfのテスト結果からわかるように、Chromeの数値配列は約5倍速く、Firefoxの数値配列は約10倍速く、IE約2倍高速です。

これは、配列に使用されている内部構造を直接明らかにするものではありませんが、この2つが互いにまったく異なることを示しています。

8
Michael Geary

受け入れられた答えは言う

[配列構成]はメモリを割り当てません!

それは必ずしも本当ではありません。オブジェクトのように順序付けされていないキーと値のペアとして配列を格納することは、すべてのルックアップが検索であるため、非常に非効率的です。エントリがソートされた場合でも、必要以上のメモリが割り当てられます。したがって、多くのエンジンは、メモリを保護してパフォーマンスを最適化するために、いくつかの異なる方法で配列を表します。これは、Michael Gearyの例でも確認できます。onyに数値が含まれる配列は最適化されます( V8の詳細 )。また、基になる表現が固定サイズであり、簡単にスケーリングできない場合、エンジンは、新しく作成された配列にいくつかの空のスロットを割り当てることを決定する場合があります。

0
Jonas Wilms