抛弃eval吧。

刚看了这篇帖子eval获得对象用法请教达人有点感想,发上来供大家参考。eval在javascript语言精粹中被作者归为js的鸡肋特性之一,刚温习了下本书,下面是该书对eval的描述:eval函数传递一个字符串给javascript编译器,并且执行其结果。它是一个被滥用得最多的javascript特性。那些对javascript语言一知半解的人们最常用到它。例如:如果你知道点表示法,但不知道下标表示法,就可能会这么写:

本文内容

  • eval
  • 隐藏的 eval
  • 安全问题
  • 结论
  • 参考资料

 

eval("myValue=myObject."+myKey+";");

eval


eval
函数是一个高等级的函数,它与任何对象都无关。其参数,如果是一个字符串表达式,那么该函数计算表达式的值;如果是一个
JavaScript 语句, 则执行。通常用在一些需要动态执行的代码中。

var foo = 1;

function test() {

    var foo = 2;

    eval('foo = 3');

    return foo;

}

test(); // 3

foo; // 1

但是,eval 只在被直接调用并且调用函数就是 eval
本身时,才在当前作用域中执行。

var foo = 1;

function test() {

    var foo = 2;

    var bar = eval;

    bar('foo = 3');

    return foo;

}

 

test(); // 2

foo; // 3

上面的代码等价于在全局作用域中调用 eval,和下面两种写法效果一样:

写法一:直接调用全局作用域下的 foo 变量

var foo = 1;

function test() {

    var foo = 2;

    window.foo = 3;

    return foo;

}

 

test(); // 2

foo; // 3

写法二:使用 call 函数修改 eval 执行的上下文为全局作用域

var foo = 1;

function test() {

    var foo = 2;

    eval.call(window, 'foo = 3');

    return foo;

}

 

test(); // 2

foo; // 3

在任何情况下,我们都应该避免使用 eval 函数。99.9% 使用 eval
的场景都有不使用 eval 的解决方案。

 

而不是这么写:

隐藏的 eval


定时函数 setTimeout 和 setInterval
都可以接受字符串作为第一个参数。但是这个特性绝对不要使用,因为它们在内部使用了
eval。

function foo() {

    // 将会被调用

}

 

function bar() {

    function foo() {

        // 不会被调用

    }

    setTimeout('foo()', 1000);

}

bar();

由于 eval 在这种情况下不是被直接调用,因此传递到 setTimeout
的字符串会自全局作用域中执行; 因此,上面的回调函数使用的不是定义在 bar
作用域中的局部变量 foo。

建议不要在调用定时器函数时,为了向回调函数传递参数而使用字符串的形式。

function foo(a, b, c) { }

 

// 不要这样做

setTimeout('foo(1,2, 3)', 1000)

 

// 可以使用匿名函数完成相同功能

setTimeout(function () {

    foo(a, b, c);

}, 1000)

 

myValue=myObject[myKey];

安全问题


eval 也存在安全问题,因为它会执行任意传给它的代码,
在代码字符串未知或者是来自一个不信任的源时,绝对不要使用 eval 函数。

eval 一般也较慢,因为它必须调用 JS 解释器,而很多其他构造方法都被现代 JS
引擎所优化。

jQuery 1.10.2 实现 globalEval 代码段,如下所示:

// Evaluates a script in a global context

// Workarounds based on findings by Jim Driscoll

// http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context

globalEval: function( data ) {

    if ( data && jQuery.trim( data ) ) {

        // We use execScript on Internet Explorer

        // We use an anonymous function so that context is window

        // rather than jQuery in Firefox

        ( window.execScript || function( data ) {

            window[ "eval" ].call( window, data );

        } )( data );

    }

},

IE 使用 window.execScript
使脚本脱离当前闭包,在全局域内运行;Firefox 则使用 window.eval
来脱离当前闭包,直接使用 eval 在当前闭包运行。

jQuery 2.0.3 实现 globalEval 代码段,若是 code
标记为严格模式,则采用注入方式;否则采用 eval,如下所示:

// Evaluates a script in a global context

globalEval: function( code ) {

    var script,

            indirect = eval;

 

    code = jQuery.trim( code );

 

    if ( code ) {

        // If the code includes a valid, prologue position

        // strict mode pragma, execute code by injecting a

        // script tag into the document.

        if ( code.indexOf("use strict") === 1 ) {

            script = document.createElement("script");

            script.text = code;

            document.head.appendChild( script ).parentNode.removeChild( script );

        } else {

        // Otherwise, avoid the DOM node creation, insertion

        // and removal by using an indirect global eval

            indirect( code );

        }

    }

},

ext js 1.6 关于 eval 的代码段,如下所示:

/**

 * Compiles the template into an internal function, eliminating the RegEx overhead.

 * @return {Ext.Template} this

 */

compile : function(){

    var me = this,

        sep = Ext.isGecko ? "+" : ",";

 

    function fn(m, name){

        name = "values['" + name + "']";

        return "'"+ sep + '(' + name + " == undefined ? '' : " + name + ')' + sep + "'";

    }

 

    eval("this.compiled = function(values){ return " + (Ext.isGecko ? "'" : "['") +

         me.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +

         (Ext.isGecko ?  "';};" : "'].join('');};"));

    return me;

},

 

使用eval形式的代码会更加难以阅读。这种形式将使得性能显著降低,因为它必须运行编译器,但也许只是为了执行一个微不足道的赋值语句。它会让JSLint失效,因此该工具检测问题的能力也会显著降低。eval函数还减弱了你的应用的安全性,因为它给被求值的文本赋予了太多的权力。而且就像with语句执行的方式一样,它降低了语言的性能。Function构造器是eval的另一种形式,所以它同样也应该被避免使用。浏览器提供的setTimeout和setInterval函数,它们能接收字符串参数或函数参数。当传递的是字符串参数时,setTimeout和setInterval会像eval那样去处理。字符串参数形式也应该被避免使用。个人在开发中没有碰到过必须用eval的时候,都可以用别的形式替换,所以让我们抛弃eval吧。

结论


绝对不要使用
eval,任何使用它的代码都会在它的工作方式,性能和安全性方面受到质疑。如果一些情况必须使用到
eval 才能正常工作,首先它的设计会受到质疑,这不应该是首选的解决方案,
一个更好的不使用 eval 的解决方案应该得到充分考虑并优先采用。

只要知道写 JavaScript 代码当使用 eval
时,存在这个问题就行,毕竟现在直接用 JavaScript
库比较多,安全性能好很多。

 

参考资料


  • mozilla 开发者网络
    eval
  • 微软 JavaScript
    严格模式

 

下载 Demo

相关文章