【譯】以 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)系作者
  • 序言:七十年代末优烧,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子链峭,更是在濱河造成了極大的恐慌畦娄,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件弊仪,死亡現(xiàn)場(chǎng)離奇詭異熙卡,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)撼短,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門再膳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)挺勿,“玉大人曲横,你說(shuō)我怎么就攤上這事〔黄浚” “怎么了禾嫉?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蚊丐。 經(jīng)常有香客問(wèn)我熙参,道長(zhǎng),這世上最難降的妖魔是什么麦备? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任孽椰,我火速辦了婚禮昭娩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘黍匾。我一直安慰自己栏渺,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布锐涯。 她就那樣靜靜地躺著磕诊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪纹腌。 梳的紋絲不亂的頭發(fā)上霎终,一...
    開(kāi)封第一講書(shū)人閱讀 51,365評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音升薯,去河邊找鬼莱褒。 笑死,一個(gè)胖子當(dāng)著我的面吹牛涎劈,可吹牛的內(nèi)容都是我干的保礼。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼责语,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼炮障!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起坤候,我...
    開(kāi)封第一講書(shū)人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤胁赢,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后白筹,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體智末,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年徒河,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了系馆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡顽照,死狀恐怖由蘑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情代兵,我是刑警寧澤尼酿,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站植影,受9級(jí)特大地震影響裳擎,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜思币,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一鹿响、第九天 我趴在偏房一處隱蔽的房頂上張望羡微。 院中可真熱鬧,春花似錦惶我、人聲如沸拷淘。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)启涯。三九已至,卻和暖如春恃轩,著一層夾襖步出監(jiān)牢的瞬間结洼,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工叉跛, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留松忍,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓筷厘,卻偏偏與公主長(zhǎng)得像鸣峭,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子酥艳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

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

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