JavaScript配列について一般的な質問がありました。 JavaScriptの配列インデックスは内部的に文字列として処理されますか?配列はJavaScriptのオブジェクトであるため、インデックスは実際には文字列であるとどこかで読みました。私はこれについて少し混乱しています、そしてどんな説明でも喜んでいます。
それは正しいので:
> var a = ['a','b','c']
undefined
> a
[ 'a', 'b', 'c' ]
> a[0]
'a'
> a['0']
'a'
> a['4'] = 'e'
'e'
> a[3] = 'd'
'd'
> a
[ 'a', 'b', 'c', 'd', 'e' ]
正式には、すべてのプロパティ名は文字列です。つまり、配列のような数値のプロパティ名は、他のプロパティ名と実際には何の違いもありません。
手順6 仕様の関連部分 を確認すると、プロパティを検索する前に、プロパティアクセサ式が常に文字列に強制変換されていることがわかります。そのプロセスは、オブジェクトが配列インスタンスであるか別の種類のオブジェクトであるかに関係なく、(正式に)実行されます。 (繰り返しますが、そうする必要がありますそれが起こっているようです。)
現在、internally、JavaScriptランタイムは自由に配列機能を自由に実装できます。
edit— _Number.toString
_で遊んで、数値から文字列への変換が行われることを示すことを考えましたが、仕様ではその特定の型が明示的に記述されていることがわかりました変換は、暗黙のキャストとそれに続く.toString()
の呼び出しではなく、内部プロセスを介して行われるものとして行われます(これはおそらくパフォーマンス上の理由から良いことです)。
はい、技術的には配列インデックスは文字列ですが、Flanaganがエレガントに彼の「決定的なガイド」にそれを置いたように:
"配列インデックスとオブジェクトプロパティ名を明確に区別すると便利です。すべてのインデックスはプロパティ名ですが、0から2までの整数のプロパティ名のみです。32-1はインデックスです。」
通常、結果が予測可能で(通常/うまくいけば)指定された結果に一致する限り、ブラウザ(またはより一般的には「script-Host」)が内部で何をするかを気にする必要はありません。実際、javascript(またはECMAScript 262)の場合は、必要な概念的な手順の観点からのみ説明されています。これにより、(意図的に)スクリプトホスト(およびブラウザー)が、指定された動作を実装するための賢い、より小さく、より高速な方法を考え出す余地が残されます。
実際、最近のブラウザーは、内部でさまざまなタイプの配列に対してさまざまなアルゴリズムを使用しています。それらが何を含んでいるか、どれだけ大きいか、順序付けられているかどうか、(jit)コンパイル時に固定されて最適化できるかどうか、またはそれらはまばらまたは密です(そうです、忍者_[]
_の代わりにnew Array(length_val)
を実行することはしばしばお金がかかります)。
あなたの思考概念(javascriptを学ぶとき)では、配列が単なる特別な種類のオブジェクトであることを知っておくと役立つかもしれません。しかし、それらは必ずしも常に同じものではありません。たとえば、次のようになります。
_var a=[];
a['4294967295']="I'm not the only one..";
a['4294967296']="Yes you are..";
alert(a); // === I'm not the only one..
_
ただし、知識のないプログラマーにとっては、配列(インデックス付き)を作成し、その配列オブジェクトにプロパティをアタッチするのは簡単で、かなり透過的です。
最良の答え(私は思う)は spec(15.4) 自体からです:
配列オブジェクト
配列オブジェクトは、特定のクラスのプロパティ名に特別な扱いをします。 プロパティ名P(文字列値の形式)は、ToString(ToUint32(P))がPと等しく、ToUint32(P)が等しくない場合にのみ配列インデックスです。 232-1。プロパティ名が配列インデックスであるプロパティは、要素とも呼ばれます。すべてのArrayオブジェクトには、値が常に2未満の非負の整数であるlengthプロパティがあります。32。 lengthプロパティの値は、名前が配列インデックスであるすべてのプロパティの名前よりも数値的に大きくなります。 Arrayオブジェクトのプロパティが作成または変更されるたびに、この不変条件を維持するために必要に応じて他のプロパティが調整されます。具体的には、名前が配列インデックスであるプロパティが追加されるたびに、長さプロパティは、必要に応じて、その配列インデックスの数値より1つ大きくなるように変更されます。また、lengthプロパティが変更されるたびに、名前が新しい長さ以上の値を持つ配列インデックスであるすべてのプロパティが自動的に削除されます。この制約は、Arrayオブジェクトの独自のプロパティにのみ適用され、プロトタイプから継承される可能性のある長さや配列インデックスのプロパティの影響を受けません。
次のアルゴリズムがtrueを返す場合、オブジェクトOはスパースであると言われます。
- 引数 "length"を指定してOの[[Get]]内部メソッドを呼び出した結果をlenとします。
0≤iの範囲の整数iごとに
a。 elemを、引数ToString(i)を使用してOの[[GetOwnProperty]]内部メソッドを呼び出した結果とします。
b。 elemが定義されていない場合は、trueを返します。Falseを返します。
事実上、ECMAScript 262仕様は、最大32ビットの符号なし_arr['42']
_または_arr[42]
_の取得/設定に関係なく、javascript-programmerに明確な配列参照を保証します。
主な違いは、たとえば(自動更新)_array.length
_、_array.Push
_、および_array.concat
_などの他の配列糖です。
はい、javascriptではオブジェクトに設定したプロパティをループすることもできますが、設定した量を読み取ることはできません(ループなし)。 そして、はい、私の知る限り、最新のブラウザ(特にchromeと呼ばれるもの(ただし正確には指定されていません))) 'small integers'はtrueで高速に邪魔されます(事前に初期化されています) )small-int配列。
たとえば、 this 関連の質問も参照してください。
編集:@ Felix Klingのテストによる(上記の彼のコメントから):
_arr[4294967294] = 42;
_の後、_arr.length
_は_4294967295
_を正しく表示します。ただし、arr.Push(21)
;を呼び出す_RangeError: Invalid array length
_をスローします。 _arr[arr.length] = 21
_は機能しますが、長さは変更されません。
この(予測可能で意図された)動作の説明は、この回答の後で明確になるはずです。
Edit2:
今、誰かがコメントをしました:
for(var i in a)console.log(typeof i)は、すべてのインデックスの「文字列」を示します。
_for in
_はjavascriptの(順序付けられていないImustadd)プロパティイテレータであるため、文字列を返すことは明らかです(そうでない場合はかなり気になります) 't)。
From [〜#〜] mdn [〜#〜] :
for..inは、インデックスの順序が重要な配列を反復処理するために使用しないでください。
配列インデックスは、整数名の列挙可能なプロパティであり、それ以外は一般的なオブジェクトプロパティと同じです。 for ... inが特定の順序でインデックスを返し、整数以外の名前のプロパティや継承されたプロパティを含む、列挙可能なすべてのプロパティを返すという保証はありません。
反復の順序は実装に依存するため、配列を反復すると、一貫した順序で要素にアクセスできない場合があります。したがって、アクセスの順序が重要な配列を反復処理する場合は、数値インデックスを持つforループ(またはArray.forEachまたはfor ... ofループ)を使用することをお勧めします。
だから..私たちは何を学びましたか?順序が重要である場合(多くの場合、配列を使用します)、JavaScriptでこの風変わりな配列を[〜#〜]必要[〜#〜]、「長さ」を持ちます番号順にループするのにかなり便利です。
次に、別の方法を考えてみましょう。オブジェクトにID /順序を指定しますが、次のID /順序(プロパティ)ごとにオブジェクトをもう一度ループする必要があります。
編集3:
誰かが次のように答えました:
_var a = ['a','b','c'];
a['4'] = 'e';
a[3] = 'd';
alert(a); // returns a,b,c,d,e
_
今私の答えの説明を使用して:何が起こったのかというと、_'4'
_は整数_4
_に強制可能であり、それは_[0, 4294967295]
_の範囲にあり、有効な配列index
(element
とも呼ばれる)になります。 var a
は配列(_[]
_)であるため、配列element4は配列elementとして追加されます。プロパティ(var a
がオブジェクト(_{}
_)の場合はどうなるでしょうか)。
配列とオブジェクトの違いをさらに概説する例:
_var a = ['a','b','c'];
a['prop']='d';
alert(a);
_
'd'が表示されずに_a,b,c
_がどのように返されるかを確認してください。
編集4:
コメント: "その場合、整数インデックスは、特殊なタイプのJSオブジェクトである配列のプロパティであるため、文字列として処理する必要があります。"
これは、用語の観点からは間違っているです。理由は次のとおりです。(文字列を表す)整数インデックス([0、4294967295]の間)は配列indexes
またはelements
を作成します。 properties
ではありません。
言うのが良いでしょう:実際の整数と整数を表すstring
(両方とも[0、4294967295]の間)は有効な配列index(そして概念的には整数と見なされるべきです)そして配列要素を作成/変更します(次の場合に返される「もの」/値(のみ)たとえば、arr.join()
またはarr.concat()
を実行します)。
他のすべてはプロパティを作成/変更します(概念的には文字列と見なす必要があります)。
ブラウザが実際に行うことは、通常は興味がないはずです。コードを指定するのが単純で明確であるほど、ブラウザが認識しなければならない可能性が高くなります。 'ああ、これを内部の実際の配列に最適化しましょう。 '。
JavaScriptには、標準配列と連想配列(またはプロパティを持つオブジェクト)の2種類の配列があります。
そう ...
var arr = [ 0, 1, 2, 3 ];
...は、インデックスが整数のみである標準配列として定義されています。何か(インデックスとして使用するもの)は整数ではないため、arr ["something"]を実行すると、基本的にarrオブジェクト(すべてがJavaScriptのオブジェクト)のプロパティを定義します。ただし、標準配列に要素を追加しているわけではありません。
どれどれ:
[1]["0"] === 1 // true
ああ、でもそれは決定的なものではありません。ランタイムが強制的になる可能性があるからです"0"
から+"0"
および+"0" === 0
。
[1][false] === undefined // true
さて、+false === 0
なので、いいえ、ランタイムは値を数値に強制変換していません。
var arr = [];
arr.false = "foobar";
arr[false] === "foobar" // true
したがって、実際には、ランタイムは値を文字列に強制変換しています。そうです、それは(外部的に)ハッシュテーブルルックアップです。