为什么 0[0] 在语法上有效?

2022-08-30 05:10:42

为什么这行在javascript中有效?

var a = 0[0];

之后,是 .aundefined


答案 1

当你这样做时,JS解释器会把第一个变成一个对象,然后尝试访问该对象的属性。0[0]0Number[0]undefined

没有语法错误,因为在此上下文中,语言语法允许属性访问语法。这种结构(使用Javascript语法中的术语)是 。0[0]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 ]

而且,同样,在遵循语法之后,最终也可以如此,我们看到这是允许的:ExpressionNumericLiteral

NumericLiteral [ NumericLiteral ]

这意味着这是语法中允许的部分,因此没有语法错误。0[0]


然后,在运行时,只要您正在读取的源是对象或具有到对象的隐式转换,就可以读取不存在的属性(它将被读取为 )。而且,数字文本确实具有对对象(Number 对象)的隐式转换。undefined

这是Javascript中经常未知的功能之一。类型和Javascript中的类型通常作为基元存储在内部(而不是完整的对象)。这些是紧凑的、不可变的存储表示(可能是为了提高实现效率而这样做的)。但是,Javascript希望你能够用属性和方法将这些基元视为对象。因此,如果您尝试访问基元上不直接支持的属性或方法,则 Javascript 会暂时将基元强制转换为适当类型的对象,并将值设置为基元的值。NumberBooleanString

当您在原语(如 )上使用类似对象的语法时,解释器会将其识别为对基元的属性访问。它对此的回应是采用第一个数字基元并将其强制转换为一个完整的对象,然后它可以访问该属性。在此特定情况下,Number 对象上的属性就是为什么这是您从 中获取的值的原因。0[0]0Number[0][0]undefined0[0]

下面是一篇关于将基元自动转换为对象以处理属性的文章:

Javascript 原语的秘密生活


以下是 ECMAScript 5.1 规范的相关部分:

9.10 检查对象可操作

如果值为 或,则抛出 TypeError,否则返回 。undefinednulltrue

enter image description here

11.2.1 属性访问器

  1. 让 baseReference 成为评估 MemberExpression 的结果。
  2. Let baseValue be GetValue(baseReference).
  3. 让 propertyNameReference 成为计算表达式的结果。
  4. Let propertyNameValue be GetValue(propertyNameReference).
  5. Call CheckObjectCoercible(baseValue).
  6. Let propertyNameString be ToString(propertyNameValue).
  7. 如果正在评估的语法生产包含在严格模式代码中,则让 strict 为 true,否则让 strict 为 false。
  8. 返回一个引用类型的值,其基值为 baseValue,其引用的名称为 propertyNameString,其严格模式标志为严格。

这个问题的一个操作部分是上面的步骤#5。

8.7.1 获取值 (V)

这描述了当被访问的值是属性引用时,它如何调用以获取任何基元的对象版本。ToObject(base)

9.9 目标

这描述了 如何将 和 基元 转换为具有相应设置 [[PrimitiveValue]] 内部属性的对象形式。BooleanNumberString


作为一个有趣的测试,如果代码是这样的:

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

它仍然不会在解析时引发 SyntaxError,因为这在技术上是合法的语法,但是当您运行代码时,它会在运行时引发 TypeError,因为当上述属性访问器逻辑应用于 的值时,它将调用或调用,如果是 或,则同时引发 TypeError。xCheckObjectCoercible(x)ToObject(x)xnullundefined


答案 2

像大多数编程语言一样,JS使用语法来解析代码并将其转换为可执行形式。如果语法中没有可应用于特定代码块的规则,则会引发 SyntaxError。否则,代码被视为有效,无论它是否有意义。

JS语法的相关部分是

Literal :: 
   NumericLiteral
   ...

PrimaryExpression :
   Literal
   ...

MemberExpression :
   PrimaryExpression
   MemberExpression [ Expression ]
   ...

由于符合这些规则,因此它被视为有效的表达式。它是否正确(例如,在运行时不抛出错误)是另一回事,但确实如此。这就是 JS 计算表达式的方式,例如:0[0]someLiteral[someExpression]

  1. 评估(可以是任意复杂的)someExpression
  2. 将文本转换为相应的对象类型(数字文本 = > , 字符串 = > 等)NumberString
  3. 使用属性名称 result(1) 调用对 result(2) 的运算get property
  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