原文地址:http://www.2ality.com/2014/01/eval.html
原文作者:Dr. Axel Rauschmayer
本博文探討在 JavaScript 中如何動(dòng)態(tài)的執(zhí)行代碼懒棉。
-
eval()
以 str 的方式運(yùn)行 JavaScript 代碼,比如:
var a = 12;
eval( 'a+5' );
17注意語(yǔ)句上下文 eval()的解析:
eval( '{ foo: 123 }' );
123
eval( '({ foo: 123 })' );
{ foo: 123 }
1.1 嚴(yán)格模式下的 eval()
對(duì)于 eval()蜡镶,理應(yīng)當(dāng)在嚴(yán)格模式下使用。在松散模式下運(yùn)行代碼會(huì)在當(dāng)前的作用域中創(chuàng)建局部變量:
function f(){
eval( 'var foo = 1' );
console.log( foo ); // 1
}
嚴(yán)格模式下就不會(huì)出現(xiàn)該情況。但是,運(yùn)行代碼仍然具有讀寫(xiě)當(dāng)前作用域中變量的權(quán)限蜕劝。你需要通過(guò)間接調(diào)用 eval() 來(lái)阻止這種權(quán)限。
1.2 全局作用域下間接執(zhí)行 eval() 有兩種調(diào)用 eval() 的方式:
- 直接方式:通過(guò)直接調(diào)用名為 "eval" 的函數(shù)轰异。
- 間接方式:使用其他的一些方式熙宇。(通過(guò) call 調(diào)用,將其以其他名字作為 window 下的一個(gè)方法存儲(chǔ)溉浙,在那里進(jìn)行調(diào)用) 之前已經(jīng)看過(guò)烫止,在當(dāng)前作用域直接使用 eval 執(zhí)行代碼
var x = 'global';
function directEval(){
'use strict';
var x = 'local';
console.log( eval('x') ); // local
}
反之,在全局作用域中間接調(diào)用 eval戳稽。
var x = 'global';
function indirectEval(){
'use strict';
var x = 'local';
// 不同方式調(diào)用 call
console.log( eval.call(null, 'x') ); // global
console.log( window.eval('x') ); // global
console.log( (1,eval)('x') ); // global (1)
var xeval = eval;
console.log( xeval('x') ); // global
var obj = { eval: eval }
console.log( obj.eval('x') ); // global
}
(1)處說(shuō)明:當(dāng)你通過(guò)一個(gè)名稱來(lái)引用一個(gè)變量的時(shí)候馆蠕,其初始值為一個(gè)所謂的引用,數(shù)據(jù)結(jié)構(gòu)分為兩部分:
1. 基礎(chǔ)是指向存儲(chǔ)變量的值的數(shù)據(jù)結(jié)構(gòu)惊奇。
2. 引用名是變量的名稱
在一個(gè)函數(shù)調(diào)用 eval 的時(shí)候互躬,該函數(shù)的調(diào)用操作符(括號(hào))遇到一個(gè)對(duì) eval 的引用可以確定被調(diào)用函數(shù)的名稱。所以此時(shí)函數(shù)會(huì)觸發(fā)一個(gè)直接的 eval 調(diào)用颂郎。當(dāng)然你可以不給調(diào)用操作符引用來(lái)強(qiáng)制間接調(diào)用 eval吼渡。這是由于在操作符運(yùn)行之前獲取引用的值來(lái)實(shí)現(xiàn)的。在 (1)標(biāo)注的那一行乓序,逗號(hào)操作符為我們實(shí)現(xiàn)的這點(diǎn)寺酪。這個(gè)運(yùn)算符運(yùn)行了第一個(gè)運(yùn)算元并返回了第二個(gè)運(yùn)算元的結(jié)果。運(yùn)算結(jié)果總是返回 值 的替劈,意味著引用已經(jīng)被解析寄雀。 間接的運(yùn)行代碼總是松散的。這是由于代碼被獨(dú)立的在當(dāng)前環(huán)境中運(yùn)行的結(jié)果陨献。
function strictFunc(){
'use strict';
var code = '(function(){ return this; }())';
var result = eval.call( null, code );
console.log( result !== undefined ); // true sloppy mode
}
2 new Function()
Function 構(gòu)造函數(shù)的函數(shù)簽名盒犹。
new Function( param1, param2, ..., paramN,funcBody );
它創(chuàng)建一個(gè)包含0個(gè)或者過(guò)個(gè)參數(shù)名為 param1 等的函數(shù),函數(shù)體為 funcBody眨业。相當(dāng)于如下方式創(chuàng)建函數(shù):
function( (param1), (param2), ..., (paramN) ){
(funcBody)
}
例如:
> var f = new Function('x', 'y', 'return x+y');
> f( 3, 4 )
與間接 eval 調(diào)用類似急膀,new Function() 創(chuàng)建的函數(shù)作用域也是全局的。
var x = 'global';
function strictFunc(){
'use strict';
var x = 'local';
var f = new Function('return x');
console.log( f() ); //global
}
以下的函數(shù)也是默認(rèn)松散模式
function strictFunc(){
'use strict';
var sl = new Function( 'return this' );
console.log( sl() !== undefined ); // true, sloppy mode
var st = new Function( '"use strict"; return this;' );
console.log( st() !== undefined ); // true, strict mode
}
-
eval() 對(duì)比 new Function()
一般來(lái)說(shuō)龄捡,使用 new Function() 運(yùn)行代碼比 eval() 更為好一些:函數(shù)的參數(shù)提供了清晰的接口來(lái)運(yùn)行代碼卓嫂,而沒(méi)有必要使用較為笨拙的語(yǔ)法來(lái)間接的調(diào)用 eval() 確保代碼只能訪問(wèn)自己的和全局的變量。
-
最佳實(shí)踐
通常:避免使用 eval() 和 new Function() 墅茉。動(dòng)態(tài)運(yùn)行代碼不但速度較慢命黔,還有潛在的安全風(fēng)險(xiǎn)呜呐。一般都可以找到更好地替代方案。
如悍募,Brendan Eich 最近在 tweete發(fā)了一個(gè)程序猿需要訪問(wèn)某一個(gè)屬性蘑辑,其屬性名被存儲(chǔ)在名為propName的變量中的反模式:
var value = eval( 'obj.'+propName );
以下方式會(huì)更OK:
var value = obj[ propName ];
你也不應(yīng)該使用 eval() 或者 new Function() 來(lái)解析 JSON格式的數(shù)據(jù)。那也是不安全的坠宴。要么使用 ECMAScript 5 內(nèi)置的對(duì)JSON的支持方法洋魂,要么使用一個(gè)類庫(kù)。
合理使用實(shí)例喜鼓。依舊有一些較為合理副砍,對(duì) eval() 和 new Function() 使用較為高級(jí)的:配置函數(shù)數(shù)據(jù)(JSON 不允許),模板庫(kù)庄岖,解析豁翎,命令行和模塊系統(tǒng)。
5. 總結(jié)
這是對(duì)于動(dòng)態(tài)運(yùn)行代碼較高層次的概述隅忿,如果想更深的了解可以看一下 kanhax的文章 “[全局eval心剥,何去何從?](http://perfectionkills.com/global-.-what-are-the-options/)”
感謝:Mariusz Nowak (@medikoo) 告知 使用Function 運(yùn)行代碼默認(rèn)形式均為松散模式背桐。
6. 參考文獻(xiàn)
【1】 [JavaScript 的 表達(dá)式和語(yǔ)句](http://www.2ality.com/2012/09/.s-vs-statements.html)
【2】 [JavaScript](http://www.2ality.com/2011/01/.s-strict-mode-summary.html)[ 嚴(yán)格模式:綜述](http://www.2ality.com/2011/01/.s-strict-mode-summary.html)
【3】 [JavaScript的 JSON API](http://www.2ality.com/2011/08/json-api.html)
轉(zhuǎn)載自 [http://blog.chinaunix.net/uid-26672038-id-4086689.html](http://blog.chinaunix.net/uid-26672038-id-4086689.html)