不用 eval 用查询window对象属性方式实现字符串函数调用

  在上文《js将字符串作为函数名调用,实现input文本框等form表单元素回车键统一事件响应》中提到,因为eval()的安全性问题,建议不使用eval(),而使用其它更安全的方式实现。那么eval()到底有哪细不足,应该如何更安全地实现?

1、eval()是一个函数,看起来更像运算符1

  eval()是一个函数,JavaScript早期版本定义了eval()函数,也就是从那时起,js设计者和解释器作者对其实施了更多限制,使其看起来更像运算符,原因有两点:
  1)现代JavaScript解释器对eval要处理的字符串进行了大量的代码分析和优化,通常情况下,如果一个函数调用了eval(),那么解释器无法对这个函数做进一步优化;
  2)eval()可以被赋予其它名称: var f = eval, g = f; 如果允许这种情况的话,那么解释器将无法放心地优化任何调用g()的函数。而当eval是一个运算符(并作为一个保留字)的时候,这种问题就可以避免掉。

2、eval()工作机制

  eval()只有一个字符串参数,如果传入的不是字符串,直接返回该参数。如果参数是字符串,它会被当成JavaScript代码进行编译,编译失败抛语法错误(SyntaxError)异常,编译成功后执行代码,并返回执行结果:
  1)有return的,返回return值;
  2)没有return的,返回undefined;
  eval()使用了调用它的变量作用域环境。简单理解就是:在函数中调用eval(),其变量作用域就是函数局部作用域,在全局中调用eval()其变量作用域就是全局作用域。看下述代码:

var foo = 1;
function test() {
    var foo = 2;
    eval('foo = 3');
    return foo;
}
test(); 		//局部作用域调用,寻找和修改的变量是foo=2, 返回值 3
foo; 		// 1
3、全局eval()

  eval()具有改变局部变量的能力,对于大部分解释器来说,当通过别名调用时,eval()会将其字符串当成顶层的全局代码执行。执行的代码可能会定义新的全局变量和全局函数,或者给全局变量赋值,但却不能使用或修改主调函数中的局部变量,看下述代码:

var foo = 1;
function test() {
    var foo = 2;
    var bar = eval;
    bar('foo = 3');
    return foo;
}
console.log("test(): ", test());	//2, 修改的是全局变量
console.log("foo: ", foo);	//3
4、eval()使用限制

  从上文可以看出eval确实有一定的安全风险,因此要杜绝一切外部输入的eval()运算。

5、不用eval(),使用查询window对象属性方式实现字符串函数调用

在上一篇博文中《js将字符串作为函数名调用,实现input文本框等form表单元素回车键统一事件响应》提到,使用eval()实现字符串函数调用存在一定安全风险。为此,使用在window对象中查找是否有该函数定义、找到后调用该函数替换实现,代码如下:

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Form表单元素回车键统一响应</title>
    <script src="http://res.maben.com/ly-assets/assets/js/lib/jquery-1.11.1.min.js"></script>
    <link rel="stylesheet" href="http://res.maben.com/ly-assets/assets/bootstrap/css/bootstrap.min.css">
  </head>
  <body>
    <div class="container" style="margin-top: 30px;">
      <div class="form-horizontal">
	<div class="form-group">
	  <label class="col-xs-2">Input框:</label>
	  <div class="col-xs-4">
	    <input class="form-control" id="user_name" enterKey="doSubmit" placeholder="输入姓名"></input>
	  </div>
	</div>
	<div class="form-group">
	  <label class="col-xs-2">Select下拉框:</label>
	  <div class="col-xs-4">
	    <select class="form-control" id="user_title" enterKey="doSubmit()">
	      <option value="讲师">讲师</option>
	      <option value="副教授">副教授</option>
	      <option value="教授">教授</option>
	    </select>
	  </div>
        </div>
	<div class="form-group">
	  <div class="col-xs-2 col-sm-offset-2">
	    <button class="btn btn-primary" type="button" onclick="doSubmit()">提交</button>
	  </div>
	</div>
      </div>
    </div>
    <script type="text/javascript">
      function doSubmit() {
	alert("name: " + $("#user_name").val() + "\ntitle: " + $("#user_title").val());
      }
      function evalEx(obj, exp) {
	var fnName = exp.indexOf("(") >=0 ? exp.substring(0, exp.indexOf("(")) : exp;
	var fn = window[fnName];
	if(typeof fn === "function") {
	  var fnArg = exp.indexOf("(") >=0 ? exp.substring(exp.indexOf("(") + 1, exp.indexOf(")")) : "", fnArgs = [];
	  var strArgs = fnArg.split(",");
	  if(strArgs.length > 0) {
	    for(var i = 0, l = strArgs.length; i<l; i++) {
	      var strArg = strArgs[i];
	      if(strArg.length > 0) {
		fnArgs.push(strArg == "this" ? obj || window : strArg);
	      }
	    }
	  }
	  fn.apply(null, fnArgs);
	}
      }
      function trim(str) {
        return str.replace(/(^\s*)|(\s*$)/g,"");
      }      
      $(window).on("load",function(){
        //enterKey="doSubmit"
        //enterKey="doSubmit()"
        //enterKey="doSubmit('keyenter')"
        //enterKey="doSubmit(this)", 代码会自动替换this为事件触发对象
	$("[enterKey]").each(function(i, elem){
	  var funcName = $(elem).attr("enterKey");
	  if(! funcName) return false;
	  $(elem).bind("keydown", function(e){
	    if(e.keyCode == 13) {
	      evalEx(e.target, funcName);
	    }
	  });
	});
      });
  </script>
</body>
</html>

  1. JavaScript权威指南 第6版. ↩︎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值