【译】以 eval() 和 new Function() 执行JavaScript代码

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

本博文探讨在 JavaScript 中如何动态的执行代码。

  1. eval()

    以 str 的方式运行 JavaScript 代码,比如:

    var a = 12;
    eval( 'a+5' );
    17

    注意语句上下文 eval()的解析:

    eval( '{ foo: 123 }' );
    123
    eval( '({ foo: 123 })' );
    { foo: 123 }

1.1 严格模式下的 eval()
对于 eval(),理应当在严格模式下使用。在松散模式下运行代码会在当前的作用域中创建局部变量:

    function f(){ 
       eval( 'var foo = 1' );
       console.log( foo ); // 1
    } 

严格模式下就不会出现该情况。但是,运行代码仍然具有读写当前作用域中变量的权限。你需要通过间接调用 eval() 来阻止这种权限。

1.2 全局作用域下间接执行 eval() 有两种调用 eval() 的方式:

  1. 直接方式:通过直接调用名为 "eval" 的函数。
  2. 间接方式:使用其他的一些方式。(通过 call 调用,将其以其他名字作为 window 下的一个方法存储,在那里进行调用) 之前已经看过,在当前作用域直接使用 eval 执行代码
   var x = 'global'; 
   function directEval(){
       'use strict';
        var x = 'local';
        console.log( eval('x') ); // local
   } 
反之,在全局作用域中间接调用 eval。 
    var x = 'global'; 
function indirectEval(){
    'use strict';
    var x = 'local';
    // 不同方式调用 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)处说明:当你通过一个名称来引用一个变量的时候,其初始值为一个所谓的引用,数据结构分为两部分: 
1. 基础是指向存储变量的值的数据结构。 
2. 引用名是变量的名称 
在一个函数调用 eval 的时候,该函数的调用操作符(括号)遇到一个对 eval 的引用可以确定被调用函数的名称。所以此时函数会触发一个直接的 eval 调用。当然你可以不给调用操作符引用来强制间接调用 eval。这是由于在操作符运行之前获取引用的值来实现的。在 (1)标注的那一行,逗号操作符为我们实现的这点。这个运算符运行了第一个运算元并返回了第二个运算元的结果。运算结果总是返回 值 的,意味着引用已经被解析。 间接的运行代码总是松散的。这是由于代码被独立的在当前环境中运行的结果。 
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 构造函数的函数签名。
new Function( param1, param2, ..., paramN,funcBody );
它创建一个包含0个或者过个参数名为 param1 等的函数,函数体为 funcBody。相当于如下方式创建函数:

    function( (param1), (param2), ..., (paramN) ){ 
        (funcBody)
    } 
例如: 
    > var f = new Function('x', 'y', 'return x+y'); 
    > f( 3, 4 ) 
与间接 eval 调用类似,new Function() 创建的函数作用域也是全局的。 
var x = 'global'; 
function strictFunc(){
   'use strict';
   var x = 'local';
   var f = new Function('return x');
   console.log( f() ); //global
} 
以下的函数也是默认松散模式 
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() 对比 new Function()

    一般来说,使用 new Function() 运行代码比 eval() 更为好一些:函数的参数提供了清晰的接口来运行代码,而没有必要使用较为笨拙的语法来间接的调用 eval() 确保代码只能访问自己的和全局的变量。

  2. 最佳实践
    通常:避免使用 eval() 和 new Function() 。动态运行代码不但速度较慢,还有潜在的安全风险。一般都可以找到更好地替代方案。
    如,Brendan Eich 最近在 tweete发了一个程序猿需要访问某一个属性,其属性名被存储在名为propName的变量中的反模式:

var value = eval( 'obj.'+propName );

    以下方式会更OK: 
var value = obj[ propName ]; 
    你也不应该使用 eval() 或者 new Function() 来解析 JSON格式的数据。那也是不安全的。要么使用 ECMAScript 5 内置的对JSON的支持方法,要么使用一个类库。 
    合理使用实例。依旧有一些较为合理,对 eval() 和 new Function() 使用较为高级的:配置函数数据(JSON 不允许),模板库,解析,命令行和模块系统。 
5. 总结 
 
这是对于动态运行代码较高层次的概述,如果想更深的了解可以看一下 kanhax的文章 “[全局eval,何去何从?](http://perfectionkills.com/global-.-what-are-the-options/)” 
 
感谢:Mariusz Nowak (@medikoo) 告知 使用Function 运行代码默认形式均为松散模式。 
6. 参考文献 
【1】 [JavaScript 的 表达式和语句](http://www.2ality.com/2012/09/.s-vs-statements.html)  
【2】 [JavaScript](http://www.2ality.com/2011/01/.s-strict-mode-summary.html)[ 严格模式:综述](http://www.2ality.com/2011/01/.s-strict-mode-summary.html)   
【3】 [JavaScript的 JSON API](http://www.2ality.com/2011/08/json-api.html)   
转载自 [http://blog.chinaunix.net/uid-26672038-id-4086689.html](http://blog.chinaunix.net/uid-26672038-id-4086689.html)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容

  • 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式。简单...
    舟渔行舟阅读 7,619评论 2 17
  • 深入理解JavaScript系列文章,包括了原创,翻译,转载,整理等各类型文章,如果对你有用,请推荐支持一把,给大...
    DaveWeiYong阅读 534评论 0 1
  • 单例模式 适用场景:可能会在场景中使用到对象,但只有一个实例,加载时并不主动创建,需要时才创建 最常见的单例模式,...
    Obeing阅读 2,017评论 1 10
  • JS基础讲解 JavaScript组成ECMAScript:解释器、翻译DOM:Document Object M...
    FConfidence阅读 545评论 0 1
  • 校园里的桃花开了,一阵风吹来,迎风飞舞的花瓣扑面而来,身后一时传来阵阵女生的惊叫声。 校园里的丁香花开了,春雨吹打...
    洋葱小白阅读 556评论 11 6