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

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

eval()

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

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

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

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

eval( '({ foo: 123 })' ); //{ foo: 123 }
严格模式下的 eval()

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

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

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

全局作用域下间接执行 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
} 

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
} 

eval() 对比 new Function()

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

最佳实践

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

var value = eval( 'obj.'+propName );
// 以下方式会更OK: 
var value = obj[ propName ]; 

你也不应该使用 eval() 或者 new Function() 来解析 JSON格式的数据。那也是不安全的。要么使用 ECMAScript 5 内置的对JSON的支持方法,要么使用一个类库。

总结

这是对于动态运行代码较高层次的概述,如果想更深的了解可以看一下 kanhax的文章 “全局eval,何去何从?

感谢:Mariusz Nowak (@medikoo) 告知 使用 Function 运行代码默认形式均为松散模式。

参考文献

【1】 JavaScript 的 表达式和语句
【2】 JavaScript 严格模式:综述
【3】 JavaScript的 JSON API

转载自 http://blog.chinaunix.net/uid-26672038-id-4086689.html

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Rhino中获取function代码和名称可以通过以下步骤实现: 1. 首先,将function定义为一个字符串,如下所示: ``` String functionStr = "function add(a, b) { return a + b; }"; ``` 2. 使用Rhino的ScriptEngine执行该字符串中的JavaScript代码,如下所示: ``` ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("JavaScript"); engine.eval(functionStr); ``` 3. 使用Rhino的Function对象获取函数的代码和名称。 ``` // 获取函数对象 Function function = (Function) engine.get("add"); // 获取函数名称 String functionName = function.getFunctionName(); // 获取函数代码 String functionCode = function.toString(); ``` 以上代码中,首先获取函数对象,然后通过函数对象的getFunctionName()方法获取函数名称,通过toString()方法获取函数代码。 完整示例代码如下: ``` import javax.script.ScriptEngineManager; import javax.script.ScriptEngine; import org.mozilla.javascript.Function; public class RhinoExample { public static void main(String[] args) throws Exception { // 定义函数字符串 String functionStr = "function add(a, b) { return a + b; }"; // 创建ScriptEngineManager对象 ScriptEngineManager manager = new ScriptEngineManager(); // 获取JavaScript引擎 ScriptEngine engine = manager.getEngineByName("JavaScript"); // 执行JavaScript代码 engine.eval(functionStr); // 获取函数对象 Function function = (Function) engine.get("add"); // 获取函数名称 String functionName = function.getFunctionName(); // 获取函数代码 String functionCode = function.toString(); // 输出函数名称和代码 System.out.println("函数名称:" + functionName); System.out.println("函数代码:" + functionCode); } } ``` 以上代码将输出以下结果: ``` 函数名称:add 函数代码function add(a,b) {return a+b;} ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值