2011-10-28 46 views
30

ECMAScript 5 spec狀態如下:JavaScript的catch子句範圍

一般一個詞法環境與的ECMAScript的代碼中的一些特定 句法結構諸如FunctionDeclaration相關聯, 一個WithStatement或TryStatement的catch子句並且每次評估此類代碼時都會創建一個新的詞彙環境 。

如果我的理解是正確的,那麼當在JavaScript中創建一個新的詞法環境,一個新的範圍進入,這就是爲什麼在函數內部聲明的變量是不可見的是功能之外:

function example() { 
    var x = 10; 
    console.log(x); //10 
} 
console.log(x); //ReferenceError 

所以在上面的函數聲明中,創建了一個新的詞法環境,這意味着x在任何可能存在的外部詞法環境中都不可用。

所以上面有關函數聲明的引用部分似乎是有道理的。然而,它也指出,新的詞法環境是一個try語句的catch子句創建:

try { 
    console.log(y); //ReferenceError so we enter catch 
} 
catch(e) { 
    var x = 10; 
    console.log(x); //10 
} 
console.log(x); //10 - but why is x in scope? 

那麼,如何一catch塊工作的範圍?對於詞彙環境是什麼我有一個基本的誤解?

+0

這個SO帖子是以某種方式相關的:http://stackoverflow.com/questions/6100230/javascript-catch-parameter-already-defined – Juri

回答

29

如果我理解它是正確的,那麼它可能意味着,在您的代碼中,

try { 
    console.log(y); //ReferenceError so we enter catch 
} 
catch(e) { 
    var x = 10; 
    console.log(x); //10 
} 

e將只存在於catch塊中。在catch塊外嘗試console.log(e);,它會拋出ReferenceError。

與WithStatement一樣,with ({x: 1, y: 2}) { },x和y將只存在於with塊中。

但這並不意味着var聲明將被綁定到最近的詞彙環境。實際上,當進入執行上下文時,var聲明將被綁定到環境。

10.5 Declaration Binding Instantiation:當輸入執行上下文時,它將查找函數聲明,參數和變量聲明,然後在執行上下文的VariableEnvironment中創建綁定。

因此,使用var聲明的任何變量都可以在函數中的任何位置訪問,而不管控制結構或函數內部何處定義。請注意,這不包括嵌套函數,因爲它們是獨立的執行上下文。

這意味着var聲明將被綁定到最接近的執行上下文。

var x = 1; 
(function() { 
    x = 5; console.log(x); // 5 
    if (false) { var x; } 
    x = 9; console.log(x); // 9 
})(); 
console.log(x); // 1 

因此,在上面的代碼中,x = 5;將設置x變量的內函數內部,因爲var x;內部if (false) { var x; }被綁定到執行功能的代碼已經在此之前的功能。

+1

啊,這很有道理。 「因此,任何使用'var' ...」段落聲明的變量都會點擊它。謝謝:) –

+0

很好的答案和很好的解釋 – contactmatt

+4

請注意,即使你在包含try-catch的函數的頂部聲明瞭'e','catch'塊中的'e'將不會是同一個變量。像函數參數一樣,它是詞法上下文本地的新變量。 –

8

dev.opera(強調)

在try-catch-finally結構是相當獨特的。與其他結構不同,它在運行時在當前範圍中創建一個新變量。每次執行catch子句時都會發生這種情況,即將捕獲的異常對象分配給一個變量。即使在同一範圍內,該變量也不存在於腳本的其他部分內。它在catch子句的開始處創建,然後在其結尾處銷燬。

所以它看起來真的只是在捕獲範圍內的是唯一的例外。其他行爲似乎是(或停留)綁定到捕獲的外部範圍(所以在全局範圍的例子中)。

try { 
    console.log(y); //ReferenceError so we enter catch 
} 
catch(e) { 
    var x = 10; 
    console.log(x); //10 
} 
console.log(e.message); //=> reference error 

ES5這些線路可能是相關的(加粗體/強調)這樣:

  1. oldEnv是運行中的執行上下文的LexicalEnvironment
  2. catchEnv是調用NewDeclarativeEnvironment傳遞 oldEnv作爲參數的結果。

此外,在該部分的最後它指出:

注意:無論怎麼控制離開塊的LexicalEnvironment是 總是恢復到以前的狀態

+0

是的,我注意到錯誤對象只在'catch'範圍內。儘管如此,仍然沒有意義。我只能假設,要麼我仍然有一個完全的誤解,或者(可能並不奇怪)規範沒有得到適當的遵守。閱讀第12.14節(http://es5.github.com/#x12.14),它似乎進一步說,任何在'catch'內聲明的變量將不在其範圍之外 –

+0

我已經添加了2行ES5標題爲「The production Catch ...」的部分。據我所知,這些錯誤只是使用'catchEnv'(6-8)在本地範圍內。其他的東西最後回到'oldEnv'。 – KooiInc

0

我對這個問題感到困惑,因爲它似乎我已經遇到過類似的問題,與函數定義中的特定catch塊無關。現在我只記得幾個星期前,我實際上也是blogged about that problem :)。

把這個代碼:

function test(){ 
    var x = "Hi"; 

    (function(){ 
    console.log(x); 
    var x = "Hi, there!"; 
    })(); 
} 

代碼:http://jsbin.com/alimuv/2/edit#javascript,live

什麼是輸出?通過快速查看它,人們可能會認爲它是「嗨」,因爲變量x的範圍取自函數的外部範圍。而是打印出undefined。原因與catch塊問題相同:詞法範圍,這意味着該範圍是在函數定義而不是在運行時創建的。

+1

這根本不談論try/catch。 – Flimm

1

由於它在ES3中被標準化,所以catch() {}子句是(據我所知)是唯一的ES2015之前的javascript構造,它創建了塊級作用域,僅僅是因爲它是附帶的唯一塊級構造論點

這就是爲什麼它被用來作爲下一代一個填充工具的JavaScript transpilers編譯這樣做的原因:

ES2015:

{ let priv = 1; } 
console.log(priv); // throws ReferenceError 

這樣:

ES5和更低:

try { throw void 0 } catch (priv) { priv = 1 } 
console.log(priv); // throws ReferenceError