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

Mic*_* M. 119 javascript

为什么这行在javascript中有效?

var a = 0[0];
Run Code Online (Sandbox Code Playgroud)

在那之后,aundefined.

jfr*_*d00 168

当你这样做时0[0],JS解释器会将第一个0转换为一个Number对象,然后尝试访问该[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    
Run Code Online (Sandbox Code Playgroud)

因此,人们可以通过这个进程跟随语法:

MemberExpression [ Expression ]
PrimaryExpression [ Expression ]
Literal [ Expression ]
NumericLiteral [ Expression ]
Run Code Online (Sandbox Code Playgroud)

并且,在遵循语法之后,同样Expression最终也可以NumericLiteral这样,我们看到这是允许的:

NumericLiteral [ NumericLiteral ]
Run Code Online (Sandbox Code Playgroud)

这意味着这0[0]是语法的允许部分,因此没有SyntaxError.


然后,在运行时,只要您从中读取undefined的源是对象或具有对对象的隐式转换,您就可以读取不存在的属性(只会被读取).并且,数字文字确实具有对对象的隐式转换(Number对象).

这是Javascript经常未知的功能之一.的类型Number,BooleanString在Javascript通常内部存储为基元(未完全成熟的对象).这些是一个紧凑的,不可变的存储表示(可能以这种方式实现了实现效率).但是,Javascript希望您能够将这些原语视为具有属性和方法的对象.因此,如果您尝试访问基元上不直接支持的属性或方法,则Javascript将暂时将基元强制转换为适当类型的对象,并将值设置为基元的值.

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

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

Javascript基元的秘密生活


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

9.10 CheckObjectCoercible

如果value为undefinednull,则抛出TypeError ,否则返回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. 如果正在评估的语法生成包含在严格模式代码中,则let strict为true,否则let strict为false.
  8. 返回类型为Reference的值,其基值为baseValue,其引用名称为propertyNameString,其严格模式标志为strict.

该问题的操作部分是上面的步骤#5.

8.7.1 GetValue(V)

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

9.9 ToObject

介绍如何Boolean,NumberString图元被转换成与[[PrimitiveValue]]内部属性相应地设置的对象的形式.


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

var x = null;
var a = x[0];
Run Code Online (Sandbox Code Playgroud)

它仍然不会在解析时抛出一个SyntaxError,因为这是技术上合法的语法,但是当你运行代码时它会在运行时抛出一个TypeError,因为当上面的Property Accessors逻辑应用于它的值时x,它会调用CheckObjectCoercible(x)或调用ToObject(x)哪个如果xnull或将抛出TypeError undefined.

  • @Michael无需更新.这是一个逗号运算符,所以它只是'0 [2]` (6认同)
  • 对于一个问题的细微差别来说,这是一个非常棒的答案. (2认同)

geo*_*org 19

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

JS语法的相关部分是

Literal :: 
   NumericLiteral
   ...

PrimaryExpression :
   Literal
   ...

MemberExpression :
   PrimaryExpression
   MemberExpression [ Expression ]
   ...
Run Code Online (Sandbox Code Playgroud)

由于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
Run Code Online (Sandbox Code Playgroud)

这是一个有效不正确的表达式的示例:

null[0]
Run Code Online (Sandbox Code Playgroud)

它解析得很好,但在运行时,解释器在第2步失败(因为null无法转换为对象)并引发运行时错误.


Dun*_*can 9

在某些情况下,您可以在Javascript中有效地下标数字:

-> 0['toString']
function toString() { [native code] }
Run Code Online (Sandbox Code Playgroud)

虽然没有立即明白你为什么要这样做,但是在Javascript中下标相当于使用点分表示法(尽管点符号限制了你使用标识符作为键).


Ran*_*832 9

我只想指出,这是有效的语法并不是Javascript独有的.大多数语言都会出现运行时错误或类型错误,但这与语法错误不同.Javascript选择在许多情况下返回undefined,其中另一种语言可能引发异常,包括在订阅没有给定名称属性的对象时.

语法不知道表达式的类型(即使是像数字文字这样的简单表达式),也允许您将任何运算符应用于任何表达式.例如,尝试下标undefinednull导致TypeErrorJavascript.这不是语法错误 - 如果从未执行过(在if语句的错误一侧),它不会导致任何问题,而语法错误根据定义总是在编译时捕获(eval,Function等) ,都算作编译).


Clo*_*lox 8

因为它是有效的语法,甚至是有效的代码来解释.您可以尝试访问任何对象的任何属性(在这种情况下,0将被强制转换为Number对象),如果存在,它将为您提供值,否则为undefined.但是,尝试访问未定义的属性不起作用,因此0 [0] [0]将导致运行时错误.尽管如此,这仍将被归类为有效语法.什么是有效语法和不会导致运行时/编译时错误的区别.