【譯】以 eval() 和 new Function() 執(zhí)行JavaScript代碼

原文地址:http://www.2ality.com/2014/01/eval.html
原文作者:Dr. Axel Rauschmayer

本博文探討在 JavaScript 中如何動(dòng)態(tài)的執(zhí)行代碼。

  1. 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() 的方式:

  1. 直接方式:通過(guò)直接調(diào)用名為 "eval" 的函數(shù)。
  2. 間接方式:使用其他的一些方式。(通過(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
} 
  1. 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)自己的和全局的變量。

  2. 最佳實(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)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 工廠模式類似于現(xiàn)實(shí)生活中的工廠可以產(chǎn)生大量相似的商品,去做同樣的事情,實(shí)現(xiàn)同樣的效果;這時(shí)候需要使用工廠模式。簡(jiǎn)單...
    舟漁行舟閱讀 7,827評(píng)論 2 17
  • 深入理解JavaScript系列文章,包括了原創(chuàng),翻譯,轉(zhuǎn)載,整理等各類型文章,如果對(duì)你有用,請(qǐng)推薦支持一把,給大...
    DaveWeiYong閱讀 625評(píng)論 0 1
  • 單例模式 適用場(chǎng)景:可能會(huì)在場(chǎng)景中使用到對(duì)象,但只有一個(gè)實(shí)例,加載時(shí)并不主動(dòng)創(chuàng)建,需要時(shí)才創(chuàng)建 最常見(jiàn)的單例模式,...
    Obeing閱讀 2,093評(píng)論 1 10
  • JS基礎(chǔ)講解 JavaScript組成ECMAScript:解釋器、翻譯DOM:Document Object M...
    FConfidence閱讀 580評(píng)論 0 1
  • 校園里的桃花開(kāi)了,一陣風(fēng)吹來(lái),迎風(fēng)飛舞的花瓣撲面而來(lái),身后一時(shí)傳來(lái)陣陣女生的驚叫聲。 校園里的丁香花開(kāi)了,春雨吹打...
    洋蔥小白閱讀 628評(píng)論 11 6