javascript operators order-of-execution

¿Por qué estos fragmentos de JavaScript se comportan de manera diferente a pesar de que ambos encuentran un error?



operators order-of-execution (3)

Considere el siguiente código:

var a = {}; a.x.y = console.log("evaluating right hand side"), 1;

El resumen aproximado de los pasos necesarios para ejecutar el código es el siguiente ref :

  1. Evaluar el lado izquierdo. Dos cosas a tener en cuenta:
    • Evaluar una expresión no es lo mismo que obtener el valor de expresión.
    • La evaluación de una ref acceso a la propiedad, por ejemplo, axy devuelve una referencia de ref consiste en un valor de base ax (indefinido) y un nombre referenciado ( y ).
  2. Evaluar el lado derecho.
  3. Obtenga el valor del resultado obtenido en el paso 2.
  4. Establezca el valor de la referencia obtenida en el paso 1 al valor obtenido en el paso 3, es decir, establezca la propiedad y de indefinido al valor. Esto se supone para lanzar una excepción TypeError ref .

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

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


El orden de las operaciones es más claro cuando explota el operador de coma dentro de la notación de corchetes para ver qué partes se ejecutan cuando:

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

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

Mirando la spec :

La producción AssignmentExpression : LeftHandSideExpression = AssignmentExpression se evalúa de la siguiente manera:

  1. Sea lref el resultado de evaluar LeftHandSideExpression.

  2. Sea rref el resultado de evaluar AssignmentExpression.

  3. Deje rval ser GetValue(rref) .

  4. Lanzar una excepción SyntaxError si ... (irrelevante)

  5. Llamar a PutValue(lref, rval) .

PutValue es lo que lanza el TypeError :

  1. Sea O sea ToObject(base) .

  2. Si el resultado de llamar al [[CanPut]] método interno de O con el argumento P es falso, entonces

    a. Si Throw es verdadero, lanza una excepción TypeError.

No se puede asignar nada a una propiedad de undefined : el método interno de [[CanPut]] de undefined siempre devolverá false .

En otras palabras: el intérprete analiza el lado izquierdo, luego analiza el lado derecho y luego arroja un error si la propiedad del lado izquierdo no puede asignarse.

Cuando tu lo hagas

a.x.y = b.e = 1

El lado izquierdo se analiza correctamente hasta que se llama a PutValue ; el hecho de que la propiedad .x evalúe como undefined no se considera hasta que se analiza el lado derecho. El intérprete lo ve como "Asignar algún valor a la propiedad" y "de indefinido", y asignarlo a una propiedad de undefined solo se lanza dentro de PutValue .

A diferencia de:

a.x.y.z = b.e = 1

El intérprete nunca llega al punto en el que intenta asignar a la propiedad z , porque primero debe resolver axy a un valor. Si axy resolviera a un valor (incluso a undefined ), estaría bien, se arrojaría un error dentro de PutValue como arriba. Pero acceder a axy un error, porque no se puede acceder a la propiedad y en undefined .


En realidad, si lees el mensaje de error correctamente, el caso 1 y el caso 2 producen errores diferentes.

Caso axy :

No se puede establecer la propiedad ''y'' de indefinido

Caso axyz :

No se puede leer la propiedad ''y'' de undefined

Supongo que lo mejor es describirlo paso a paso en un inglés fácil.

Caso 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) }

Caso 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) }

En comentarios, Solomon Tam encontró ecma-international.org/ecma-262/5.1/#sec-11.13 .