なぜ0 [0]が構文的に有効なのですか?


119

この行がJavaScriptで有効なのはなぜですか?

var a = 0[0];

その後、aですundefined


4
true[0]または ""[0]
Hacketo

24
@CodeAngry公平に言うと、JavaScriptはHTML内で生まれたものであり、それが全体として「私に好きなものを投げて、私がそれを理解しようとする」ということを始めたのはHTMLです。
Niet the Dark Absol 2015年

8
@NiettheDarkAbsol公平に言うと、構文理にかなっているので単に間違っています(まだそれほどではありません)。これは"0"new Number(0)オブジェクトの名前付きプロパティを取得するだけです。
meandre 2015年

aは常に未定義であるというのは誤った仮定です。0[0]値を返すことは完全に可能です
ルーンFS

@meandre 0["toString"]それは素晴らしいです、指摘してくれてありがとう。
ジョナサン

回答:


169

あなたが行うと0[0]、JSインタプリタは最初に変わります0Number、オブジェクトとその後、アクセスしようと[0]しているオブジェクトのプロパティをundefined

プロパティのアクセス構文0[0]は、このコンテキストの言語文法で許可されているため、構文エラーはありません。この構造(JavaScript文法の用語を使用)はNumericLiteral[NumericLiteral]です。

ES5 ECMAScript仕様のセクションA.3の言語文法の関連部分は次のとおりです。

Literal ::
    NullLiteral
    BooleanLiteral
    NumericLiteral
    StringLiteral
    RegularExpressionLiteral

PrimaryExpression :
    this
    Identifier
    Literal
    ArrayLiteral
    ObjectLiteral
    ( Expression )

MemberExpression :
    PrimaryExpression
    FunctionExpression
    MemberExpression [ Expression ]
    MemberExpression . IdentifierName
    new MemberExpression Arguments    

したがって、この進行を通じて文法に従うことができます。

MemberExpression [ Expression ]
PrimaryExpression [ Expression ]
Literal [ Expression ]
NumericLiteral [ Expression ]

そして、同様に、Expression最終的にNumericLiteralは文法を順守した後もそうなる可能性があるため、これが許可されていることがわかります。

NumericLiteral [ NumericLiteral ]

つまり、これは0[0]文法で許可されている部分であり、SyntaxErrorはありません。


次に、実行時に、読み込まれundefinedているソースがオブジェクトであるか、オブジェクトに暗黙的に変換されている限り、存在しないプロパティ(単にとして読み込まれます)を読み込むことができます。また、数値リテラルには実際にオブジェクト(Numberオブジェクト)への暗黙の変換があります。

これは、JavaScriptのよく知られていない機能の1つです。タイプNumberBooleanおよびStringJavaScriptは通常、内部的にプリミティブとして格納されます(本格的なオブジェクトではありません)。これらは、コンパクトで不変のストレージ表現です(おそらく、実装効率のためにこの方法で行われます)。しかし、Javascriptでは、これらのプリミティブをプロパティやメソッドを持つオブジェクトのように扱えるようにしたいと考えています。そのため、プリミティブで直接サポートされていないプロパティまたはメソッドにアクセスしようとすると、JavaScriptは一時的にプリミティブを適切なタイプのオブジェクトに強制し、値をプリミティブの値に設定します。

などのプリミティブでオブジェクトのような構文を使用する0[0]と、インタプリタはこれをプリミティブのプロパティアクセスとして認識します。これに対するその応答は、最初の0数値プリミティブを取得してNumber[0]プロパティにアクセスできる完全なオブジェクトに変換することです。この特定のケースでは[0]、Numberオブジェクトのプロパティがundefinedそのため、取得する値です0[0]

プロパティを処理するためのプリミティブからオブジェクトへの自動変換に関する記事を次に示します。

JavaScriptプリミティブの秘密の生活


ECMAScript 5.1仕様の関連部分は次のとおりです。

9.10 CheckObjectCoercible

値がundefinedまたnullはの場合はTypeErrorをスローし、それ以外の場合はを返しますtrue

ここに画像の説明を入力してください

11.2.1プロパティアクセサー

  1. baseReferenceをMemberExpressionの評価結果とします。
  2. baseValueをGetValue(baseReference)とします。
  3. propertyNameReferenceを式の評価結果とします。
  4. propertyNameValueをGetValue(propertyNameReference)にします。
  5. CheckObjectCoercible(baseValue)を呼び出します。
  6. propertyNameStringをToString(propertyNameValue)にします。
  7. 評価される構文生成がstrictモードのコードに含まれている場合は、strictをtrue、それ以外の場合はstrictをfalseにします。
  8. 基本値がbaseValueであり、参照名がpropertyNameStringであり、strictモードフラグがstrictであるReferenceタイプの値を返します。

この質問の有効な部分は、上記のステップ5です。

8.7.1 GetValue(V)

これは、アクセスされている値がプロパティ参照である場合ToObject(base)に、プリミティブのオブジェクトバージョンを取得するために呼び出す方法を示しています。

9.9 ToObject

これは、方法を説明しBooleanNumberそしてStringプリミティブは、それに応じて[[PrimitiveValue]]内部プロパティを設定してオブジェクト形式に変換されます。


興味深いテストとして、コードが次のようである場合:

var x = null;
var a = x[0];

これは技術的に法的な構文であるように、それはまだ、解析時にSyntaxErrorを投げていないだろうが、あなたがコードを実行するときに、プロパティアクセサロジック以上の値に適用した場合ので、実行時にTypeError例外を投げるだろうx、それが呼び出すCheckObjectCoercible(x)か、呼び出しToObject(x)ました両方の場合はTypeErrorをスローしますxですnullundefined


0[1,2]も有効ですが、どういう意味ですか?(質問を更新します)
Michael M.15年

また、プロパティにアクセスできない場合nullundefinedまったく問題がない場合でもプロパティにアクセスできるため、構文エラーは発生しません。
user4642212 2015年

6
@Michael更新する必要はありません。それはカンマ演算子なので、それだけです0[2]
Amit Joki

1
カンマ演算子:1と2の両方を評価し1,2ますが、2を返します
user4642212

2
それは、質問のニュアンスに対するそのような素晴らしい答えです。
tbh__ 2015年

20

ほとんどのプログラミング言語と同様に、JSは文法を使用してコードを解析し、実行可能な形式に変換します。文法に、特定のコードチャンクに適用できるルールがない場合、SyntaxErrorがスローされます。それ以外の場合、コードは意味があるかどうかにかかわらず、有効であると見なされます。

JS文法の関連部分は

Literal :: 
   NumericLiteral
   ...

PrimaryExpression :
   Literal
   ...

MemberExpression :
   PrimaryExpression
   MemberExpression [ Expression ]
   ...

0[0]これらのルールに準拠しているため、有効な式と見なされます。それが正しいかどうか(実行時にエラーをスローしないなど)は別の話ですが、そうです。これは、JSが次のような式を評価する方法ですsomeLiteral[someExpression]

  1. 評価someExpression(これは任意の複雑な場合があります)
  2. リテラルを対応するオブジェクトタイプに変換します(数値リテラル=> Number、文字列=> Stringなど)
  3. get propertyプロパティ名result(1)でresult(2)の操作を呼び出します
  4. 結果を破棄する(2)

したがって0[0]、次のように解釈されます

index = 0
temp = Number(0)
result = getproperty(temp, index) // it's undefined, but JS doesn't care
delete temp
return result

有効であるが正しくない式の例を次に示します。

null[0]

正常に解析されますが、実行時にステップ2でインタープリターが失敗し(nullオブジェクトに変換できないため)、ランタイムエラーがスローされます。


1
それだけではありません。 var x = null; var a = x[0];構文エラーは生成しませんが、実行時にTypeErrorをスローします。
jfriend00 2015年

@ jfriend00:それが問題のすべてではありませんが、追加されました。
georg 2015年

結果は未定義である必要はありません。0[0]未定義の代わりに値を返すことは可能です
Rune FS

9

Javascriptで数値に有効に添字を付けることができる状況があります。

-> 0['toString']
function toString() { [native code] }

なぜこれを実行するのかはすぐにはわかりませんが、JavaScriptでの添え字付けは、ドット表記法を使用することと同じです(ただし、ドット表記法はキーとしての識別子の使用に制限されます)。


@AmitJokiこれは(0).toString(関数を呼び出さずに)と同じです。数値型のプロパティです。
user4642212 2015年

@AmitJokiは「なぜこの行が有効なのか」という質問に答えるためです。
ダンカン

@Duncanですが、これは「ブラケット表記とは」のほうが多く、OPはそれを知っていると思います。それはNumberオブジェクトとして解釈だと、その旨0プロパティがアクセスされ、それが存在しないためには、undefinedあるより jfriend00で説明したように正しいです。
Amit Joki 2015年

@AmitJoki 0[0]は未定義を返す誤った仮定です。可能性は高いですが、そうである必要はありません
Rune FS

9

これは有効な構文であり、JavaScriptに固有のものではないことに注意してください。ほとんどの言語には実行時エラーまたは型エラーがありますが、それは構文エラーと同じではありません。Javascriptは、指定された名前のプロパティを持たないオブジェクトに添え字を付ける場合など、別の言語が例外を発生させる可能性のある多くの状況で未定義を返すことを選択します。

構文は式のタイプ(数値リテラルのような単純な式でさえ)を知らないため、任意の演算子を任意の式に適用できます。たとえば、Javascriptで添え字を付けようとしundefinedたり、null引き起こしたりTypeErrorします。これは構文エラーではありません-これが決して実行されない場合(ifステートメントの間違った側にある場合)は、問題は発生しませんが、構文エラーは定義上、常にコンパイル時にキャッチされます(eval、Functionなど)。 、すべてコンパイルとしてカウントされます)。


8

それは有効な構文であり、解釈される有効なコードでさえあるからです。任意のオブジェクトの任意のプロパティにアクセスしようとすることができ(この場合、0はNumberオブジェクトにキャストされます)、存在する場合は値を提供します。それ以外の場合は未定義です。ただし、undefinedのプロパティにアクセスしようとしても機能しないため、0 [0] [0]は実行時エラーになります。ただし、これは有効な構文として分類されます。有効な構文と実行時/コンパイル時エラーを引き起こさないものには違いがあります。


3

構文が有効であるだけでなくundefined、ほとんどすべての場合ではなくても、ほとんどの場合、結果はそうである必要はありません。JSは、最も純粋なオブジェクト指向言語の1つです。ほとんどのいわゆるOO言語はクラス指向であり、一度作成されたオブジェクトの形式(クラスに関連付けられている)を変更することはできず、オブジェクトの状態のみを変更することができます。JSでは、オブジェクトの状態とフォームを変更できますが、これは思ったより頻繁に行うことができます。この機能は、コードを誤用した場合に、かなりあいまいなコードになります。数値は不変であるため、オブジェクト自体を変更することはできません。状態やフォームではないため、変更できます

0[0] = 1;

これは、1を返すが実際には何も割り当てない有効な割り当て式です0。数値は不変です。それ自体は少し奇妙です。何も割り当てない有効で正しい(実行可能な)割り当て式を使用できます(*)。ただし、数値のタイプは変更可能なオブジェクトであるため、タイプを変更でき、変更はプロトタイプチェーンにカスケードされます。

Number[0] = 1;
//print 1 to the console
console.log(0[0]);
//will also print 1 to the console because all integers have the same type
console.log(1[0]); 

もちろんそれはまともな使用カテゴリからは程遠いですが、他のシナリオではオブジェクトの機能を拡張することは実際には非常に理にかなっているため、言語はこれを可能にするように指定されています。これは、jQueryプラグインがjQueryオブジェクトにフックして例を示す方法です。

(*)実際には値1をオブジェクトのプロパティに割り当てますが、その(一時的な)オブジェクトを参照する方法はないため、nexx GCパスで収集されます


3

JavaScriptでは、すべてがオブジェクトであるため、インタープリターはそれを解析するときに、0をオブジェクトとして扱い、0をプロパティとして返そうとします。trueまたは ""(空の文字列)の0番目の要素にアクセスしようとすると、同じことが起こります。

0 [0] = 1を設定しても、プロパティとその値はメモリに設定されますが、0にアクセスしている間は数値として扱われます(ここでは、オブジェクトと数値の扱いを混同しないでください)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.