1. ホーム
  2. javascript

[解決済み] なぜ0[0]は構文上有効なのですか?

2022-09-20 16:24:26

疑問点

なぜこの行はjavascriptで有効なのでしょうか?

var a = 0[0];

その後に aundefined .

どのように解決するのですか?

以下のような場合 0[0] とすると、JSインタプリタが最初の 0Number オブジェクトを作成し、そのオブジェクトにアクセスします。 [0] プロパティにアクセスします。 undefined .

プロパティアクセスの構文である 0[0] はこの文脈では言語文法で許可されているため、構文エラーは発生しません。 この構造(Javascriptの文法における用語を使用)は NumericLiteral[NumericLiteral] .

から言語文法の該当箇所を セクション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 つです。 型は Number , BooleanString は、通常プリミティブとして内部的に保存されます(本格的なオブジェクトではありません)。 これらは、コンパクトで不変のストレージ表現です(おそらく実装の効率化のためにこのようにしたのでしょう)。 しかし、Javascriptは、これらのプリミティブをプロパティやメソッドを持つオブジェクトのように扱えるようにしたいと考えています。 そのため、プリミティブで直接サポートされていないプロパティやメソッドにアクセスしようとすると、Javascriptは一時的にプリミティブを適切なオブジェクトの種類に強制変換し、その値をプリミティブの値として設定します。

プリミティブでオブジェクトのような構文、例えば 0[0] のようなオブジェクトのような構文を使うと、 インタープリタはこれをプリミティブのプロパティアクセスとして認識します。 これに対する反応は、最初の 0 数値プリミティブを取り出し、それを本格的な Number オブジェクトにアクセスできるようにします。 [0] プロパティにアクセスできるようになります。 この具体的な例では [0] プロパティは、Number オブジェクトに undefined であるため、この値は 0[0] .

ここでは、プロパティを扱う目的で、プリミティブをオブジェクトに自動変換する記事を紹介します。

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


ECMAScript 5.1仕様の関連する部分を紹介します。

9.10 CheckObjectCoercible (オブジェクトの互換性)

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

<イグ

11.2.1 プロパティアクセサ

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

この質問に対する操作部分は、上記のステップ#5です。

8.7.1 値を取得する (V)

これは、アクセスされる値がプロパティ参照であるとき、どのように呼び出すかを記述しています。 ToObject(base) で、任意のプリミティブのオブジェクトバージョンを取得することができます。

9.9 ToObject

これは Boolean , NumberString プリミティブは、[[PrimitiveValue]]内部プロパティが適宜設定され、オブジェクト形式に変換されます。


面白いテストとして、もしコードがこのようなものであったなら。

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

これは技術的に合法な構文なのでパース時にSyntaxErrorは投げませんが、コードを実行したときにTypeErrorを投げます。 x の値に適用されるとき、それは CheckObjectCoercible(x) を呼び出すか ToObject(x) を呼び出すと、どちらもTypeErrorを投げます。 xnull または undefined .