JavaScript中的eval()函數詳解(轉載)

今天在看js的命名空間時,發現用到了一個eval()函數,覺得很有必要仔細看一下這個函數,在網上查資料,發現這篇博客寫得很仔細,就轉過來和大家分享一下。如原博主認爲侵犯了版權,請聯繫我及時刪除。

以下是博客正文:

和其他很多解釋性語言一樣,JavaScript同樣可以解釋運行由JavaScript源代碼組成的字符串,併產生一個值。JavaScript通過全局函數eval()來完成這個工作

eval(“1+2”);//-> 3
  動態判斷源代碼中的字符串是一種很強大的語言特性,幾乎沒有必要在實際中應用。如果你使用了eval(),你應當仔細考慮是否真的需要使用它。

一、eval()是一個函數還是一個運算符

eval()是一個函數,但由於它已經被當成運算符來對待了。。JavaScript語言的早期版本定義了eval函數,現代JavaScript解釋器進行了大量的代碼分析和優化。而eval的問題在於,用於動態執行的代碼通常來講不能分析,換句話說,如果一個函數調用了eval,那麼解釋器將無法對這個函數做進一步優化,而將eval定義爲函數的另一個問題是,它可以被賦予其他的名字,var f=eval;那麼解釋器就無法放心的優化任何調用了f()的函數。而當eval是一個運算符的時候,就可以避免這些問題。

二、eval()

eval()只有一個參數。如果傳入的參數不是字符串,它直接返回這個函數。如果參數是字符串,它會把字符串當成JavaScript代碼進行編譯,如果編譯失敗者拋出一個語法錯誤異常。如果編譯成功,則開始執行這一段代碼,並返回字符串中的最後一個表達式會或語句的值,如果最後一個表達式或語句沒有值,則最終返回undefined。如果字符串拋出一個異常,這個異常將把該調用傳遞給eval()。

關於eval最重要的是,它使用了調用它的變量作用域環境。也就是說,它查找變量的值和定義新變量和函數的操作和局部作用域中的代碼完全一樣。如果一個函數定義了一個局部變量x,然後調用eval(“x”),它會返回局部變量的值。如果它調用eval(“x=1”),它會改變局部變量的值。如果函數調用了eval(“var y=2;”),它聲明瞭一個新的局部變量y,同樣地,一個函數可以通過如下代碼聲明一個局部變量:

eval(“function f(){return x+1;}”);

如果在最頂層的代碼中調用eval,當然,它會作用於全局變量和全局函數。

需要注意的是,傳遞給eval的字符串必須在語法上將的通,不能通過eval往函數中任意粘貼代碼片段,比如:eval(“return ;”)是沒有意義的,因爲return只有在函數中才起到作用,並且事實上,eval的字符串執行時的上下文環境和調用函數的上下文環境是一樣的,這不能使其作爲函數的一部分來運行。如果字符串作爲一個單獨的腳本是有語義的,那麼將其傳遞給eval作參數是完全沒有問題的,否則,eval會拋出語法錯誤異常。

三、全局eval()

eval()具有更改佈局變量的能力,這對於JavaScript優化器來說是一個很大的問題。然而作爲一種權宜之計,JavaScript解釋器針對那些調用了eval的函數所做的優化並不多。但當腳本定義了eval的一個別名,且用另一個名稱調用它,JavaScript解釋器又會如何工作呢?爲了讓JavaScript解釋器的實現更加簡化,ECMAScript3標準規定了任何解釋器都不允許對eval賦予別名。如果eval函數通過別名調用的話,則會拋出一個EavlError異常。

實際上,大多數的實現並不是這麼做的。當通過別名調用時,eval會將其字符串當成頂層的全局代碼來執行。執行的代碼可能會定義新的全局變量和全局函數,或者給全局變量賦值,但卻不能使用或者修改主調函數中的局部變量,因此,這不會影響到函數內的代碼優化。

ECMAScript5是反對使用EavlError的,並且規範了eval的行爲,“直接的eval”,當直接使用非限定的“eval”名稱來調用eval()函數時,通常稱爲“直接eval”。直接調用eval()時,它總是在調用它的上下文作用域內執行。其他的間接調用則使用全局對象作爲其上下文作用域,並且無法讀、寫、定義局部變量和函數。下面有一段示例代碼:

代碼如下:

var geval=eval;                //使用別名調用evla將是全局eval
var x="global",y="global";    //兩個全局變量
function f(){                //函數內執行的是局部eval
    var x="local";            //定義局部變量
    eval("x += ' chenged';");//直接使用eval改變的局部變量的值
    return x;                //返回更改後的局部變量
}
Function g(){                //這個函數內執行了全局eval
    var y="local";
    geval("y += ' changed';"); //直接調用改變了全局變量的值
    return y;
}
console.log(f(),x);            //改變了佈局變了,輸出 “local changed global”
console.log(g(),y);            //改變了全局變量,輸出    “local global changed”

全局的eval的這些行爲不僅僅是處於代碼優化其的需要而作出的一種折中方案,它實際上是一種非常有用的特性,它允許我們執行那些對上下文沒有任何依賴的全局腳本代碼段。真正需要eval來執行代碼段的場景並不多見。但當你真的意識到它的必要性的時候,你更可能會使用全局eval而不是局部eval。

四、嚴格eval()

ECMAScript5嚴格模式對eval()函數的行爲施加了更多的限制,甚至對標識符eval的使用也施加了限制。當在嚴格模式下調用eval時,或者eval執行的代碼段以“Use strict” 指令開始,這裏的eval是私有上下文環境中的局部eval。也就是說,在嚴格模式下,eval執行的代碼段可以查詢或更改局部變量,但不能在局部作用域中定義新的變量或函數。

此外,嚴格模式將“eval”列爲保留字,這讓eval()更像一個運算符。不能用一個別名覆蓋eval()函數。並且變量名,函數名。函數參數或者異常捕獲的參數都不能取名爲eval。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章