Programming

이러한 JavaScript 코드 조각이 둘 다 오류가 발생하더라도 다르게 작동하는 이유는 무엇입니까?

procodes 2020. 8. 9. 10:27
반응형

이러한 JavaScript 코드 조각이 둘 다 오류가 발생하더라도 다르게 작동하는 이유는 무엇입니까?


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


실제로 오류 메시지를 제대로 읽으면 사례 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)
}

사례 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)
}

의견에서 Solomon Tam 은 할당 작업에 대한이 ECMA 문서를 찾았습니다 .


다음과 같은 경우 실행되는 부분을 확인하기 위해 대괄호 표기법 내부의 쉼표 연산자를 이용하면 작업 순서가 더 명확 해집니다.

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

상기 찾고 사양 :

생산 AssignmentExpression : LeftHandSideExpression = AssignmentExpression은 다음과 같이 평가됩니다.

  1. Let lref be the result of evaluating LeftHandSideExpression.

  2. Let rref be the result of evaluating AssignmentExpression.

  3. Let rval be GetValue(rref).

  4. Throw a SyntaxError exception if... (irrelevant)

  5. Call PutValue(lref, rval).

PutValue is what throws the TypeError:

  1. Let O be ToObject(base).

  2. If the result of calling the [[CanPut]] internal method of O with argument P is false, then

    a. If Throw is true, then throw a TypeError exception.

Nothing can be assigned to a property of undefined - the [[CanPut]] internal method of undefined will always return false.

In other words: the interpreter parses the left-hand side, then parses the right-hand side, then throws an error if the property on the left-hand side can't be assigned to.

When you do

a.x.y = b.e = 1

The left hand side is successfully parsed up until PutValue is called; the fact that the .x property evaluates to undefined is not considered until after the right-hand side is parsed. The interpreter sees it as "Assign some value to the property "y" of undefined", and assigning to a property of undefined only throws inside PutValue.

In contrast:

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

The interpreter never gets to the point where it tries to assign to the z property, because it first must resolve a.x.y to a value. If a.x.y resolved to a value (even to undefined), it would be OK - an error would be thrown inside PutValue like above. But accessing a.x.y throws an error, because property y cannot be accessed on undefined.


Consider the following code:

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

The rough outline of steps required to execute the code is as follows ref:

  1. Evaluate the left hand side. Two things to keep in mind:
    • Evaluating an expression is not the same as getting the value of expression.
    • Evaluating a property accessor ref e.g. a.x.y returns a reference ref consisting of base value a.x (undefined) and referenced name (y).
  2. Evaluate the right hand side.
  3. Get the value of result obtained in step 2.
  4. Set the value of the reference obtained in step 1 to the value obtained in step 3 i.e. set property y of undefined to the value. This is supposed to throw a TypeError exception ref.

참고URL : https://stackoverflow.com/questions/54646467/why-do-these-snippets-of-javascript-behave-differently-even-though-they-both-enc

반응형