2016-08-17 2 views
4

이 ES2015 모듈과 노드 v4.4.5에서 실행될 때의 동작을 고려하십시오.스위치 설명 및 ES2015의 범위

'use strict' 

const outer = 1 

switch ('foo') { 
    case 'bar': 
    const heyBar = 'HEY_BAR' 
    break 
    case 'baz': 
    const heyBaz = 'HEY_BAZ' 
    break 
    default: 
    const heyDefault = 'HEY_DEFAULT' 
} 
console.log(
    outer, // 1, makes sense, same top-level scope 
    heyBar, // undefined. huh? I thought switch did NOT create a child scope 
    heyBaz, // undefined. huh? I thought switch did NOT create a child scope 
    heyDefault) // 'HEY_DEFAULT' makes sense 

이것은 내게 내부적으로 일치하지 않는 것처럼 보입니다. switch 문이 어휘 범위를 만들지 않았다면 모든 hey* 변수가 주요 범위에 속하게되어 모든 변수가 일관되게 동작 할 것으로 기대됩니다. switch 문이 어휘 범위를 만들었다면 일관성을 유지하기를 기대하지만 case 절에 선언 된 변수는 자식 범위에있는 것처럼 동작하지만 default 절의 변수는 바깥쪽에있는 것처럼 동작합니다 범위.

제 질문은입니다. switch 문에 관련된 하위 범위가 있습니까? 그렇다면 어떻게 동작하는지 자세히 알려주십시오.

노드 v6.4.0에서의 동작은 다릅니다. 스위치 블록이 실제로 하위 블록 범위를 만드는 것처럼 보입니다.

ReferenceError: heyBar is not defined 

그리고 훨씬 이해하기 쉽습니다.

+0

Btw, 각각의 경우에 블록 스코프를 추가하면 케이스 별 범위를 만들 수 있습니다 :'case 'bar': {const heyBar = 'HEY_BAR'; 휴식}'. – ftor

답변

9

나는 당신의 행동을 전혀 재현 할 수 없다.

ReferenceError: heyBar is not defined 

나에게 올바른 동작처럼 보인다 : 나는 즉시 ReferenceError (그 문제에 대한 노드 6.4.0 현재 파이어 폭스)를 얻는다. AFAIK switch 대괄호가있는 문 블록을 작성하면 블록 범위 엔티티의 어휘 범위가 작성됩니다. case 문 자체는 자체 블록을 만들지 않습니다.

우리가 switch 문에서 foo 경우에이 예제를 확장하면, 그것은뿐만 아니라 ReferenceError를 던졌습니다 :

'use strict' 

const outer = 1 

switch ('foo') { 
    case 'bar': 
    const heyBar = 'HEY_BAR' 
    break 
    case 'baz': 
    const heyBaz = 'HEY_BAZ' 
    break 
    case 'foo': 
    const heyFoo = 'HEY_FOO' 
    break 
    default: 
    const heyDefault = 'HEY_DEFAULT' 
} 
console.log(
    outer, 
    heyFoo, 
    heyBar, 
    heyBaz, 
    heyDefault) // ReferenceError: heyFoo is not defined 

참조

Here is the section in the spec: 13.12.11 Runtime Semantics: Evaluation

5. Let blockEnv be NewDeclarativeEnvironment(oldEnv). 
6. Perform BlockDeclarationInstantiation(CaseBlock, blockEnv). 

CaseBlock입니다 sw의 block 문 가려움증.

모든 블록 수준의 선언 (예 : let, const 또는 블록 레벨 함수 선언을) 스위치 문 (switch { <-here-> })의 블록에 새로운 블록 환경을 만들고 인스턴스화 :

이 약에 변환합니다.

+0

흥미 롭습니다. 노드 v4와 노드 v6간에 변경된 것 같습니다. –

+1

당신은'6.4.0' 노드를 사용하고 있다고 썼습니다? 아마도 v6가 v4보다 ES2016 사양에 더 가깝습니다. – nils

+0

차이점을 이해하려고 노력하고 있습니다. 또한 ES2015의 투기 동작이 무엇인지에 대한 명확한 문서를 찾고 있습니다. –

3

heyBar이 없으면 위의 코드는 ReferenceError: heyDefault is not defined을 엄격 모드에서 처리하고 그렇지 않은 경우 ReferenceError: heyBar을 던집니다.

switchswitch (...) { ... } 문에 대한 범위를 만들고 case에 대한 범위는 만들어지지 않습니다. the reference을 참조하십시오.

1

switch 본문은 새로운 블록 범위를 만듭니다. 각 개별 case 절 또는 default 절은 자동으로 새 블록 범위를 작성하지 않습니다.

범위 지정 및 switch 문을 이해하기위한 확실한 참조는 물론 ES2016 specification입니다. 그러나 그것이 정말로 무엇을 말하고 있는지를 알아내는 일이 있습니다. 내가 그 명세에서 따를 수있는 것을 통해 당신을 걸어 나갈 것이다.

CaseBlock: 
    { CaseClauses } 
    { CaseClauses DefaultClause CaseClauses } 

CaseClauses: 
    CaseClause 
    CaseClauses CaseClause 

CaseClause: 
    case Expression : StatementList 

DefaultClause: 
    default : StatementList 

는 그래서 CaseBlock가하십시오 CaseBlock이있다,

SwitchStatement: 
    switch (Expression) CaseBlock 

그리고 :

정의 중요한 용어

는 우선, 스위치 문이 정의된다 switch 문 (모든 경우를 포함하는 코드)의 본문

이것은 우리가 기대하는 바이지만 위에 정의 된 CaseBlock이라는 용어는 다른 스펙 참조에서 중요합니다.

CaseBlock 그런 다음 13.2.14 Runtime Semantics: BlockDeclarationInstantiation(code, env), 우리는 CaseBlock 생성되는 새로운 범주를 일으키는 것을 알 수

새로운 범위를 생성한다. 블록 또는 CaseBlock 생산 새로운 선언적 환경 레코드 평가

은 환경의 인스턴스화 만들어 블록마다 바인딩 블록 선언 변수, 상수 함수 발생기 함수 또는 클래스 범위가 기록. CaseBlock 이후

switch 문의 본문이이 switch 문의 본문은 하나 개의 새로운 블록 범위 (새로운 /리스 CONST 선언 컨테이너)를 생성하는 수단이다.

CaseClause는 기존 범위 (자신의 범위를 작성하지 않습니다)

그런 다음, 13.12.6 Static Semantics: LexicallyScopedDeclarations에, 그것은 어휘 범위 선언은 구문 분석시에는 인터프리터에 의해 수집하는 방법에 대해 설명을 추가합니다. 여기 사양의 실제 텍스트입니다 (설명이 따를 것이다) :

CaseBlock : {CaseClauses DefaultClause CaseClauses}

  1. 첫 CaseClauses가있는 경우, 선언이 첫 번째 CaseClauses의 LexicallyScopedDeclarations하자.
  2. 다른 예외는 선언을 새로운 빈 목록으로 만듭니다.
  3. DefaultClause의 LexicallyScopedDeclarations 요소를 선언에 추가합니다.
  4. 두 번째 CaseClauses가 없으면 선언을 반환합니다.
  5. Else는 두 번째 CaseClauses의 LexicallyScopedDeclarations 요소를 선언에 추가 한 결과를 반환합니다.

CaseClauses : CaseClauses CaseClause 선언 CaseClauses의 LexicallyScopedDeclarations 수

  1. 하자.
  2. CaseClause의 LexicallyScopedDeclarations 요소를 선언에 추가합니다.
  3. 선언을 반환합니다.

CaseClause : CASE 식 :

  1. StatementList가 있으면 StatementList

    는 StatementList의 LexicallyScopedDeclarations를 반환한다.
  2. 그렇지 않으면 새로운 빈 목록을 반환합니다.

DefaultClause : 기본 :

  1. StatementList가 존재하는 경우 StatementList

    는 StatementList의 LexicallyScopedDeclarations을 반환합니다.
  2. 그렇지 않으면 새로운 빈 목록을 반환합니다.

그래서, 기본적으로 어떤이가 말하는 것은 처음 caseClause는 LexicallyScopedDeclarations 객체를 생성한다는 것이다. 그리고 그 뒤에 오는 각 DefaultClause 또는 CaseClause는 해당 선언 객체에 추가됩니다. 이것은 스펙이 범위 내에서 모든 선언을 작성하는 방법을 설명합니다.

CaseClause은 기존 선언 개체에 추가되지만 자체 선언은 만들지 않습니다. 즉, 자체 범위를 만들지 않고 대신 포함 범위를 사용합니다.

물론 CaseClause 내에 블록을 정의 할 수 있으며 해당 블록은 자체 범위가되지만 CaseClause에는 블록 선언이 필요하지 않으므로 기본적으로 새 범위를 만들지 않습니다.

런타임 의미 : 평가

그런 일이 13.12.11 Runtime Semantics: Evaluation

SwitchStatement에서 런타임에 작동하는 방법에 대한 자세한 설명이 있습니다 : 수 exprRef 스위치 (표현) CaseBlock

  1. 하자가 표현식을 평가 한 결과
  2. switchValue를 사용 하시겠습니까? GetValue (exprRef).
  3. oldEnv를 실행중인 실행 컨텍스트의 LexicalEnvironment로 설정합니다.
  4. blockEnv를 NewDeclarativeEnvironment (oldEnv)로 둡니다.
  5. BlockDeclarationInstantiation (CaseBlock, blockEnv)을 수행하십시오.
  6. 실행중인 실행 컨텍스트의 LexicalEnvironment를 blockEnv로 설정하십시오.
  7. R을 switchValue 인수로 CaseBlock의 CaseBlockEvaluation을 수행 한 결과라고합시다.
  8. 실행중인 실행 컨텍스트의 LexicalEnvironment를 oldEnv로 설정하십시오. 여기
  9. 복귀 R.

수술 단계는 새로운 블록 환경이 생성된다 CaseBlock 4-5 단계이다. 13.12.11의 텍스트에서 계속하면 CaseBlock 안에 CaseClause에 대한 새로운 블록 환경이 생성되지 않습니다.

CaseClause : CASE 식 : StatementList

  1. 돌아 StatementList을 평가 한 결과.

그래서, 당신이 가지고 있습니다. CaseBlock은 새 블록 범위를 만듭니다. A CaseClause은 (사용자가 CaseClause 내에서 명시 적으로 블록을 정의하지 않는 한) 없습니다.