15

重複の可能性:
Javascript配列はまばらですか?

私は現在JavaScriptを学んでおり、いくつかの簡単な紹介とチュートリアルを読んでいます。Arrayオブジェクトを見ていると、C / Java / Scala / ...のような他の言語から来た、非常に奇妙なことに私を驚かせるいくつかの詳細に出くわしました。


したがって、配列を次のように定義するとします。

var arr = ['foo','bar','qux']

今割り当てます

arr[5] = 'baz'

その結果、配列は次のようになります。

arr
>> ["foo", "bar", "qux", undefined, undefined, "baz"]

そして長さは予想通りです

arr.length
>> 6

JavaScriptは、配列を必要な長さ(6)に拡張し、実際に値を割り当てたものを除いて、新しい項目は未定義に設定されています。

低レベルの観点からは、これはメモリ的には恐ろしいことです。通常、配列はメモリ内の連続した範囲になります。配列を大きくするには、通常、配列全体を十分なサイズの新しいメモリ位置にコピーする必要があります。これは非常にコストのかかる操作です。

配列のコピーは非常にコストがかかり、これらすべての「未定義」の値でメモリスペースが浪費されるため、これはJavaScriptエンジンが実行していることではない可能性が高いことを認識しています。

誰かがドアの後ろで実際に何が起こっているのか教えてもらえますか?

  • 配列は実際にはある種のリンクリストですか?
  • 「未定義」の配列項目は実際にありますか?
  • ほとんどが「未定義」で埋め尽くされている大きな配列を処理するのにどれくらいの費用がかかりますか?
4

5 に答える 5

16

JavaScriptの最初のバージョンでは、配列はありませんでした。それらは後に、その「すべてのオブジェクトの母」のサブクラスとして導入されました:Object。これを行うことで、これを非常に簡単にテストできます。

var foo = [1,2,3,4];
for (var n in foo)
{//check if n is equal (value and type) to itself, coerced to a number
    console.log(n === +(n) ? 'Number' : 'String');
}

これにより、何度も何度もログStringに記録されます。内部的には、すべての数値キーが文字列に変換されます。Lengthプロパティは、最も高いインデックスをフェッチし、それに1を追加するだけです。これ以上何もない。配列を表示すると、オブジェクトが繰り返され、キーごとに、他のオブジェクトと同じルールが適用されます。最初にインスタンスがスキャンされ、次にプロトタイプがスキャンされます。コードを少し変更すると、次のようになります。

var foo = [1,2,3,4];
foo[9] = 5;
for (var n in foo)
{
    if (foo.hasOwnProperty(n))
    {//check if current key is an array property
        console.log(n === +(n) ? 'Number' : 'String');
    }
}

undefinedインスタンス内にも、基になるプロトタイプにも対応する値が見つからなかったため、配列には5つの独自のプロパティしかなく、キー4〜8は未定義であることがわかります。つまり、配列は実際には配列ではありませんが、同様に動作するオブジェクトです。

Timが述べたように、そのオブジェクト内に存在する未定義のプロパティを持つ配列インスタンスを持つことができます。

var foo = [1,2,undefined,3];
console.log(foo[2] === undefined);//true
console.log(foo[99] === undefined);//true

しかし、繰り返しになりますが、違いがあります。

console.log((foo.hasOwnProperty('2') && foo[2] === undefined));//true
console.log((foo.hasOwnProperty('99') && foo[99] === undefined));//false

要約、あなたの3つの主な質問:

  • 配列はオブジェクトであり、数値インスタンスを使用してそのプロパティを参照できます。

  • undefined値はありません。JSがオブジェクトとプロトタイプをスキャンし、探しているものが見つからない場合のデフォルトの戻り値にすぎません。 「申し訳ありませんが、私の本では、あなたが私に尋ねたことは定義されていません。」それが言うことです。

  • 主に未定義の配列を操作してもオブジェクト自体のサイズには影響しませんが、プロトタイプもスキャンする必要があるため、未定義のキーへのアクセスは非常にわずかに遅くなる可能性があります。

アップデート:

Ecma stdを引用するだけです:

15.4配列オブジェクト
配列オブジェクトは、特定のクラスのプロパティ名に特別な扱いをします。プロパティ名P(文字列値の形式)は、ToString(ToUint32(P))がPに等しく、ToUint32(P)が2 ^32-1に等しくない場合にのみ、配列インデックスになります。プロパティ名が配列インデックスであるプロパティは、要素とも呼ばれます。すべての配列オブジェクトには、値が常に2^32未満の非負の整数であるlengthプロパティがあります。lengthプロパティの値は、名前が配列インデックスであるすべてのプロパティの名前よりも数値的に大きくなります。Arrayオブジェクトのプロパティが作成または変更されるたびに、この不変条件を維持するために必要に応じて他のプロパティが調整されます。具体的には、名前が配列インデックスであるプロパティが追加されるたびに、必要に応じて長さプロパティが変更されます。その配列インデックスの数値より1つ多くなります。また、lengthプロパティが変更されるたびに、名前が新しい長さ以上の値を持つ配列インデックスであるすべてのプロパティが自動的に削除されます。この制約は、Arrayオブジェクトの独自のプロパティにのみ適用され、プロトタイプから継承される可能性のある長さや配列インデックスのプロパティの影響を受けません。

次のアルゴリズムがtrueを返す場合、オブジェクトOはスパースであると言われます
。1。引数"length"を指定してOの[[Get]]内部メソッドを呼び出した結果をlenとします。
2.0≤iaの範囲の整数iごと
    に。elemを、引数ToString(i)を使用してOの[[GetOwnProperty]]内部メソッドを呼び出した結果とします。
     b。elemが未定義の場合、trueを返します。
3.falseを返します。

于 2012-09-28T13:05:02.740 に答える
4

配列は、オブジェクトの順序付きリストにすぎません。JavaScriptでは、すべてがオブジェクトであるため、配列は、私たちが知っているように実際には配列ではありません:)

あなたはここで小さな内部を見つけることができます。

大規模な配列での作業について疑問がある場合は...「クライアント側」での計算が少ないほど、ページの速度が速くなることを忘れないでください。

于 2012-09-28T12:50:14.187 に答える
1

回答:

  1. lengthJavaScriptの配列は、魔法のプロパティと追加のプロトタイプメソッド(push()など)を備えたオブジェクト(つまり、順序付けられていないプロパティのコレクション)とまったく同じです。
  2. いいえ、未定義のアイテムはありません。JavaScriptには、inこれを証明するために使用できるプロパティの存在をテストする演算子があります。したがって、次の配列の場合:var arr = ['foo']; arr[2] = 'bar';2 in arrreturnstrueおよび1 in arrreturns false
  3. スパース配列は、スパース配列で実際に定義されているプロパティの数と同じ長さのデンス配列よりも多くのメモリを使用する必要はありません。未定義のプロパティを反復処理する場合にのみ、スパース配列を操作する方がコストがかかります。
于 2012-09-28T12:59:34.933 に答える
0

ほとんどのjavascript実装は、配列をキーとして配列インデックスを持つバイナリツリーまたはハッシュテーブルのフレーバーとして配列を実装するため、未定義のオブジェクトの広い範囲がメモリを消費しません。

于 2012-09-28T13:07:35.510 に答える
0

配列は[値、ポインタ]の2つの部分に分かれていると言われました。したがって、arr[2]のポインタはnullです。5を追加すると、アドレスがnullから3番を指し、4番を指し、5番を指し、null(配列の終わり)になります。

iveが実際にチェックしたことがないので、これがどれほど真実かわかりません。しかし、それは理にかなっているようです。

したがって、ac型配列のように計算を行うことはできません(つまり、値4に到達するには、開始メモリポイント+ 4x(メモリ内のオブジェクト量)を実行するだけです)が、配列peiceをpeiceごとに追跡することで実行できます。

于 2012-09-28T13:24:06.917 に答える