2016-08-17 68 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語句沒有創建詞法範圍,我希望所有的變量都是主範圍的一部分,並且所有的行爲都是一致的。如果switch語句確實創建了一個詞法作用域,我仍然認爲它們是一致的,但case子句中聲明的變量的行爲與它們在子作用域中的行爲類似,而default子句中的變量的行爲與它在外部範圍。

我的問題是 switch語句中是否包含任何子範圍,如果有的話,它們的行爲細節是什麼?

在節點v6.4.0中,行爲是不同的。它看起來像開關塊確實創建一個子塊範圍。

ReferenceError: heyBar is not defined 

而且這似乎更直接瞭解。

+0

順便說一句,如果你爲每個案例添加一個塊範圍,你可以創建一個每個案例範圍:'case'bar':{const heyBar ='HEY_BAR';打破}'。 – ftor

回答

9

我根本無法重現你的行爲。我立即得到一個ReferenceError(節點6.4.0和Firefox的電流爲此事):

ReferenceError: heyBar is not defined 

這似乎是正確的行爲給我。帶括號的AFAIK switch語句DO會創建一個塊,從而爲塊範圍實體提供一個詞法範圍。 case聲明本身不會創建自己的塊。

如果我們用foo情況下,在switch聲明擴展這個例子,它拋出一個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的塊聲明癢的情況。

這大致轉換爲:

創建switch語句(switch { <-here-> })的塊的新塊的環境和實例化所有塊級聲明(如letconst或塊級別函數聲明)。

+0

有趣。在節點v4和節點v6之間好像有變化。 –

+1

你寫過你使用節點'6.4.0'?這一變化可能意味着v6比v4更接近ES2016規範。 – nils

+0

我試圖理解它們之間的差異。我還在尋找關於ES2015的規範行爲的清晰文檔。 –

3

如果沒有heyBar,則上面的代碼在嚴格模式下拋出ReferenceError: heyDefault is not defined,否則拋出ReferenceError: heyBar

switch創建switch (...) { ... }語句的作用域,範圍爲case語句未創建。見the reference

1

switch語句的主體將創建一個新的塊範圍。每個單獨的case子句或default子句都不會自動創建新的塊範圍。

理解範圍界定和switch聲明的權威性參考當然是ES2016 specification。然而,有些工作要弄清楚它真正說的是什麼。我會盡力引導你瞭解我可以遵循的規範。

定義重要條款

首先,switch語句定義是這樣的:

SwitchStatement: 
    switch (Expression) CaseBlock 

而且,一個CaseBlock是這樣的:

CaseBlock: 
    { CaseClauses } 
    { CaseClauses DefaultClause CaseClauses } 

CaseClauses: 
    CaseClause 
    CaseClauses CaseClause 

CaseClause: 
    case Expression : StatementList 

DefaultClause: 
    default : StatementList 

所以一個CaseBlockswitch聲明(包含所有情況的代碼)的正文。

這就是我們所期待的,但上面定義的術語CaseBlock對其他規範的引用很重要。

CaseBlock創造新的適用範圍

然後,在13.2.14 Runtime Semantics: BlockDeclarationInstantiation(code, env),我們可以看到一個CaseBlock導致創建一個新的範圍。

當一個塊或CaseBlock產量評估了新的聲明 環境記錄被創建並針對每個塊綁定作用域 變量,常量函數,發電機功能,或類在 的塊中聲明環境中的被實例化的記錄。

由於CaseBlockswitch語句體,這意味着switch語句體創建一個新的塊範圍(對新讓/ const聲明的容器)。

CaseClause添加到現有的範圍(不創建自己的範圍內)

然後,在13.12.6 Static Semantics: LexicallyScopedDeclarations,它描述詞法範圍的聲明如何在分析時解釋收集。下面是從規範的實際文本(說明如下):

CaseBlock:{CaseClauses DefaultClause CaseClauses}

  1. 如果第一CaseClauses存在,讓申報是第一CaseClauses的LexicallyScopedDeclarations。
  2. 否則讓聲明成爲一個新的空列表。
  3. 附加到聲明DefaultClause的LexicallyScopedDeclarations的元素。
  4. 如果第二個CaseClauses不存在,則返回聲明。
  5. 否則返回附加到聲明中第二個CaseClauses的LexicallyScopedDeclarations的元素的結果。

CaseClauses:CaseClauses CaseClause

  1. 設聲明是CaseClauses的LexicallyScopedDeclarations。
  2. 附加到聲明CaseClause的LexicallyScopedDeclarations的元素。
  3. 退貨聲明。

CaseClause:殼體表達式:語句列表

  1. 如果語句列表存在,則返回語句列表的LexicallyScopedDeclarations。
  2. 否則返回一個新的空列表。

DefaultClause:默認:語句列表

  1. 如果語句列表存在,返回語句列表的LexicallyScopedDeclarations。
  2. 否則返回一個新的空列表。

所以,基本上是什麼要說的是,第一caseClause創建LexicallyScopedDeclarations對象。然後,每個DefaultClause或CaseClause附加到該聲明對象。這是規範描述如何在範圍內構建所有聲明的方式。

A CaseClause附加到現有的聲明對象,它不創建它自己的。這意味着它不會創建自己的範圍,而是使用包含範圍。

當然,您可以在CaseClause中定義一個塊,然後該塊將成爲其自己的範圍,但CaseClause不需要塊聲明,因此默認情況下它不創建新範圍。

運行時語義:評估

然後,有事情在運行時是如何工作的13.12.11 Runtime Semantics: Evaluation

SwitchStatement進一步解釋:開關(表達)CaseBlock

  1. 讓exprRef是評估表達式的結果。
  2. 讓switchValue是?的GetValue(exprRef)。
  3. 讓oldEnv成爲正在運行的執行上下文的詞法環境。
  4. 讓blockEnv爲NewDeclarativeEnvironment(oldEnv)。
  5. 執行BlockDeclarationInstantiation(CaseBlock,blockEnv)。
  6. 將正在運行的執行上下文的LexicalEnvironment設置爲blockEnv。
  7. 設R是使用參數switchValue對CaseBlock執行CaseBlockEvaluation的結果。
  8. 將正在運行的執行上下文的詞法環境設置爲oldEnv。
  9. 返回R.

手術這裏步驟是其中用於CaseBlock創建一個新的塊的環境的步驟4和5。如果您在11.12.12的文本中繼續,則在CaseBlock內沒有爲CaseClause創建新的塊環境。

CaseClause:CASE表達式:語句列表

  1. 返回評估語句列表的結果。

所以,你有它。 A CaseBlock創建一個新的塊範圍。 A CaseClause不會(除非您自己在CaseClause中明確定義了一個塊)。