在JavaScript中,可以使用eval()函數(shù)來解析字符串中的JavaScript代碼,并返回相應(yīng)的代碼執(zhí)行結(jié)果:
console.log(eval("42 * 2"));//84
就本質(zhì)而言,eval()是JavaScript全局對象的一個函數(shù)。比如,上述代碼等價于:
console.log(this.eval("42 * 2"));//84
不過在使用eval()語句時,一般都采用上述第一種做法,也即忽略全局對象直接使用eval()。
eval()的使用
基于以下兩個原因,除非確實需要,不然應(yīng)盡量避免在代碼中使用eval()語句:
1.從邏輯上來說,字符串應(yīng)該用于儲存程序運行過程中的內(nèi)容、信息,而不應(yīng)該用于儲存具體的計算邏輯。
2.由于eval()參數(shù)為字符串,而對于一段字符串無法詞法進(jìn)行,因此對于eval()調(diào)用語句,JavaScript解釋器無法進(jìn)行優(yōu)化。
eval()的返回值
eval()的返回值遵循以下規(guī)則:
1.如果eval()的參數(shù)不是字符串,那么eval()將直接返回參數(shù)。
2.如果eval()的參數(shù)是字符串,那么eval()將這個字符串解析成代碼后進(jìn)行執(zhí)行,并返回最后一行代碼執(zhí)行的結(jié)果。
3.如果字符串無法解析成合法的代碼,eval()將拋出SyntaxError錯誤。
4.如果字符串能夠被解析成合法的代碼,但是在執(zhí)行這一代碼過程中報錯,那么這個錯誤會上報給eval()語句,并由eval()拋出。
console.log(eval([1,2,3]));//[1, 2, 3]
console.log(typeof eval([1,2,3]));//object
console.log(eval("42 */ 2"));//SyntaxError
console.log(eval("42 * 2; 22 * 3;"));//66. eval returns the result of last expression/statement
console.log(eval("null.toString()"));//TypeError, exception in eval-ed code will be propagated outside eval().
變量環(huán)境(variable environment)
JavaScript中eval()有一個重要的特性:eval()參數(shù)字符串中的代碼可以訪問外部代碼中的變量,同時也可以將參數(shù)字符串代碼中新建的變量暴露給外部代碼。也即,如果eval()參數(shù)字符串可以被合法解析,那么JS會將解析后的代碼替換掉eval()所在的那一行:
//variable environment
var a = 108;
console.log(eval("function double(x){return x*2;} a = double(a)"));
console.log(a);//216
console.log(double(33));//66
值得注意的是,實現(xiàn)上述特性的前提是eval()參數(shù)字符串中的代碼可以被合法解析。除了代碼語法正確外,JS還要求eval()參數(shù)字符串中的代碼必須“自成一體”:僅就參數(shù)字符串中的代碼而言,代碼必須是有意義的。比如,無法將諸如“return;”這樣的字符串傳給eval()函數(shù):
function test(){
var s = "test";
eval("return s;");
}
test();//SyntaxError: return not in function
如果直接使用eval()函數(shù),那么eval()參數(shù)字符串中的代碼所訪問的變量是eval()語句所在function的那些變量,也即eval()函數(shù)所使用的變量環(huán)境是“本地變量環(huán)境”。如果不直接使用eval()函數(shù),而是使用一個同樣指向eval()函數(shù)的新變量,那么相應(yīng)參數(shù)字符串中的代碼所訪問的變量均為全局變量,也即eval()函數(shù)所使用的變量環(huán)境是“全局變量環(huán)境”:
//local variable environment and global variable environment
var renamed = eval;
var x = "origin", y = "origin";
function f(){
var x = "new";
eval("x += 'Changed';");
return x;
}
function g(){
var y = "new";
renamed("y += 'Changed';");
return y;
}
console.log(f(), x);//newChanged origin
console.log(g(), y);//new originChanged
不過,值得注意的是,IE6、7、8中的行為與此不同。在IE6、7、8中,即使eval()函數(shù)被重命名,所使用的也依然是“本地變量環(huán)境”。