2016-01-13 18 views
3

我想通過評估用戶提供的字符串來構建一個簡單的REPL。它似乎大部分工作,除了像「函數f(){...}」這樣的輸入,它對將來演示中哪些函數是可見的沒有影響。在玩了一下之後,我只能得出結論,我根本不瞭解eval。下面是一段簡短的片段,展示一些神祕的行爲:麻煩評估函數定義(...在JavaScript中)

var xeval = eval; 

function silly() {} 

eval("function good() {}"); 

function baffleMe() { 
    eval("alsoGood = function() {}"); 
    eval("function notSoGood() {}"); 
    xeval("function hope() {}"); 
    xeval("function crushedHope() { silly(); }"); 
} 

baffleMe(); 

good();   // Okay. 
alsoGood();  // Okay. 
notSoGood(); // ReferenceError: notSoGood is not defined 
hope();   // Why does this even work? 
crushedHope(); // ReferenceError: silly is not defined 

請問誰能解釋這些結果? (重複性同時在最新的Chrome和Firefox)

[編輯]

爲了澄清,只有當代碼在JavaScript控制檯或者工具如的jsfiddle被執行,而不是當嵌入腳本的最後調用失敗標籤。對接受的答案的評論包含對此的解釋。

+0

查看'window.eval'(Node中的'global.eval')和'eval' – elclanrs

+0

緊密相關:[global。eval無法訪問詞法範圍中的變量](http://stackoverflow.com/q/31459180/710446) – apsillers

+0

如果您要創建REPL,則應該將字符串evals放入'try catch'語句中,並且如果錯誤消息是'意外的輸入結束',那麼提示輸入更多的代碼,直到'eval'工作,或者返回一個不同的錯誤。 –

回答

1

我會盡量解釋:

好是在全球範圍內評估:

good();   // Okay. 

Alsogood有沒有變種的定義,因此正在對全球範圍內定義:

alsoGood();  // Okay. 

NotSoGood在函數範圍內定義,因此不存在於全局範圍內:

notSoGood(); // ReferenceError: notSoGood is not defined 

希望通過EVAL對全球範圍內的封閉參考評價,從而對全球範圍的評估:

hope();   // Why does this even work? 

CrushedHope是一樣的希望,但傻應該在這種情況下被定義爲:

crushedHope(); // ReferenceError: silly is not defined 

正如在下面的評論所提到的,使用的jsfiddle和當代碼是包裹着window.onload當傻是在問題未定義。

+0

但是爲什麼在粉碎的希望中不可見? – user3026691

+0

@ user3026691'crushedHope'不會產生您在Chrome和Firefox控制檯中測試時描述的錯誤。你在哪裏看到這個錯誤? – apsillers

+1

@ user3026691啊,我看到了問題:如果你的代碼不是全局的(例如,封裝在一個'(function(){...})()'IIFE中,那麼'傻'不是全局的。 ,'crushedHope' *是全局的,所以它不能在IIFE內部看到你本地定義的'愚蠢'功能。 – apsillers

1

這是ECMAScript5的一項功能。要quote MDN

如果您使用eval函數間接,經由比其他的eval參考調用它,爲的ECMAScript 5的它工作在全球範圍內,而不是本地範圍;這意味着,例如,函數聲明創建全局函數,並且被評估的代碼無法訪問被調用範圍內的局部變量。

因此直接調用eval()在本地範圍內創建函數。 (使用a=something方法會自動創建一個全局。)但是當您通過xeval()間接調用它時,它會得到全局評估。

silly未定義的最終結果取決於您的評估範圍。如果您在控制檯中,它似乎有不同的範圍。如果你創建一個測試HTML頁面,它可以正常工作。