为什么这些JavaScript代码片段表现不同,即使它们都遇到错误?

Kev*_*kin 106 javascript operators order-of-execution

var a = {}
var b = {}

try{
  a.x.y = b.e = 1 // Uncaught TypeError: Cannot set property 'y' of undefined
} catch(err) {
  console.error(err);
}
console.log(b.e) // 1
Run Code Online (Sandbox Code Playgroud)

var a = {}
var b = {}

try {
  a.x.y.z = b.e = 1 // Uncaught TypeError: Cannot read property 'y' of undefined
} catch(err) {
  console.error(err);
}

console.log(b.e) // undefined
Run Code Online (Sandbox Code Playgroud)

Yon*_*uan 151

实际上,如果您正确读取错误消息,则案例1和案例2会抛出不同的错误.

案例a.x.y:

无法设置未定义的属性"y"

案例a.x.y.z:

无法读取未定义的属性'y'

我想最好用简单的英语逐步执行来描述它.

情况1

// 1. Declare variable `a`
// 2. Define variable `a` as {}
var a = {}

// 1. Declare variable `b`
// 2. Define variable `b` as {}
var b = {}

try {

  /**
   *  1. Read `a`, gets {}
   *  2. Read `a.x`, gets undefined
   *  3. Read `b`, gets {}
   *  4. Set `b.z` to 1, returns 1
   *  5. Set `a.x.y` to return value of `b.z = 1`
   *  6. Throws "Cannot **set** property 'y' of undefined"
   */
  a.x.y = b.z = 1
  
} catch(e){
  console.error(e.message)
} finally {
  console.log(b.z)
}
Run Code Online (Sandbox Code Playgroud)

案例2

// 1. Declare variable `a`
// 2. Define variable `a` as {}
var a = {}

// 1. Declare variable `b`
// 2. Define variable `b` as {}
var b = {}

try {

  /**
   *  1. Read `a`, gets {}
   *  2. Read `a.x`, gets undefined
   *  3. Read `a.x.y`, throws "Cannot **read** property 'y' of undefined".
   */
  a.x.y.z = b.z = 1
  
} catch(e){
  console.error(e.message)
} finally {
  console.log(b.z)
}
Run Code Online (Sandbox Code Playgroud)

在评论中,Solomon Tam发现了ECMA关于分配操作的文档.


Cer*_*nce 56

当您在括号表示法中使用逗号运算符来查看在以下情况下执行哪些部分时,操作顺序会更清晰:

var a = {}
var b = {}

try{
 // Uncaught TypeError: Cannot set property 'y' of undefined
  a
    [console.log('x'), 'x']
    [console.log('y'), 'y']
    = (console.log('right hand side'), b.e = 1);
} catch(err) {
  console.error(err);
}
console.log(b.e) // 1
Run Code Online (Sandbox Code Playgroud)

var a = {}
var b = {}

try {
  // Uncaught TypeError: Cannot read property 'y' of undefined
  a
    [console.log('x'), 'x']
    [console.log('y'), 'y']
    [console.log('z'), 'z']
    = (console.log('right hand side'), b.e = 1);
} catch(err) {
  console.error(err);
}

console.log(b.e) // undefined
Run Code Online (Sandbox Code Playgroud)

看看规格:

生产AssignmentExpression : LeftHandSideExpression = AssignmentExpression评估如下:

  1. 让lref成为评估LeftHandSideExpression的结果.

  2. 让rref成为评估AssignmentExpression的结果.

  3. 让rval成为GetValue(rref).

  4. 如果......(无关紧要)抛出一个SyntaxError异常

  5. 打电话PutValue(lref, rval).

PutValue是什么抛出TypeError:

  1. 让O成为ToObject(base).

  2. 如果[[CanPut]]用参数P 调用O 的内部方法的结果为假,那么

    一个.如果Throw为true,则抛出TypeError异常.

没有任何东西可以分配给undefined- 的[[CanPut]]内部方法undefined将始终返回false.

换句话说:解释器解析左侧,然后解析右侧,如果左侧的属性无法分配,抛出错误.

当你这样做

a.x.y = b.e = 1
Run Code Online (Sandbox Code Playgroud)

左侧被成功解析,直到PutValue被调用; 在解析右侧之后才考虑.x属性评估的事实undefined.解释器将其视为"为属性分配一些值"y"未定义",并将其分配给undefined仅在内部抛出的属性PutValue.

相反:

a.x.y.z = b.e = 1
Run Code Online (Sandbox Code Playgroud)

解释器永远不会达到它试图分配给z属性的程度,因为它首先必须解析a.x.y为一个值.如果a.x.y解析为一个值(甚至是undefined),那就没问题 - PutValue如上所述会抛出错误.但是访问 a.x.y会引发错误,因为y无法访问属性undefined.

  • 好的逗号操作员技巧 - 从未想过以这种方式使用它(当然只用于调试)! (20认同)
  • S /解析/评估/ (2认同)