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


107

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


3
@NinaScholz : 이해가 안 돼요. 구문 오류가 없습니다. 그래서 나는 그것을 가정하고 먼저 실행 b.z = 1하고 b.e = 1(오른쪽 연관성이 주어지면 =) a.x.y.z = ...실행하고 실패합니다. b할당이 한 경우에는 통과하지만 다른 경우에는 통과 하지 않는 이유는 무엇입니까?
Amadan

3
@NinaScholz 우리는 재산 y이 존재하지 않는다는 데 동의합니다 a.x; 그러나 그것은 두 경우 모두 사실입니다. 두 번째 경우에 오른쪽 할당을 방지하지만 첫 번째 경우는 방지하지 않는 이유는 무엇입니까? 실행 순서가 어떻게 다른가요? (구문 오류의 타이밍이 런타임 오류의 타이밍과 매우 다르기 때문에 구문 오류를 언급했습니다.)
Amadan

@Amadan 코드 실행 후 오류가 발생하고 변수 이름을 다시 사용하여 값을 확인하는 것보다
Code Maniac

2
Javascript가 할당 작업을 진행하는 방법을 발견했습니다. ecma-international.org/ecma-262/5.1/#sec-11.13
Solomon Tam

2
이론적 인 관점에서 보면 흥미롭지 만, 이것은 예상치 못한 행동의 "이런 코드를 작성하지 않는 이유"범주에 속합니다.
John Montgomery

답변:


152

실제로 오류 메시지를 제대로 읽으면 사례 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 문서를 찾았습니다 .


57

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

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. 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

왼쪽 은 호출 될 때까지 성공적으로 구문 분석 됩니다 PutValue. .x속성이 평가 된다는 사실 undefined은 오른쪽이 구문 분석 될 때까지 고려되지 않습니다. 인터프리터는 "정의되지 않은"y "속성에 일부 값 할당"으로 간주하고 undefined내부에 던지는 속성 에만 할당합니다 PutValue.

대조적으로 :

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

인터프리터는 z속성 에 할당하려는 지점에 도달하지 못합니다. 먼저 a.x.y값을 확인 해야하기 때문 입니다. a.x.y값 (심지어 undefined)으로 해결 되면 정상 PutValue입니다. 위와 같이 내부에 오류가 발생 합니다. 그러나에서 속성에 액세스 할 수 없기 때문에 액세스 a.x.y 하면 오류가 발생 y합니다 undefined.


20
좋은 쉼표 연산자 트릭-그런 식으로 사용한다고 생각한 적은 없습니다 (물론 디버깅 전용)!
ecraig12345

2
S / 해석 / 평가 /
BERGI

3

다음 코드를 고려하십시오.

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

단계의 대략적인 개요는 다음과 같이 코드가 실행하는 데 필요한 REF :

  1. 왼쪽을 평가하십시오. 기억해야 할 두 가지 사항 :
    • 식을 평가하는 것은 식의 값을 얻는 것과 다릅니다.
    • 예를 들어 속성 접근 자 참조를 평가하면 기본 값 (정의되지 않음)과 참조 된 이름 ( ) 으로 구성된 a.x.y참조 참조가 반환 됩니다.a.xy
  2. 오른쪽을 평가하십시오.
  3. 2 단계에서 얻은 결과 값을 가져옵니다.
  4. 1 단계에서 얻은 참조 값을 3 단계에서 얻은 값으로 설정합니다. 즉 y, undefined 속성 을 값으로 설정 합니다. TypeError 예외 ref가 발생 합니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.