zoukankan      html  css  js  c++  java
  • JavaScript 为什么不要使用 eval

    本文内容

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

     

    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)

     

    安全问题


    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(/(
    |
    )/g, '\n').replace(/'/g, "\'").replace(this.re, fn) +
             (Ext.isGecko ?  "';};" : "'].join('');};"));
        return me;
    },

     

    结论


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

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

     

    参考资料


     

    下载 Demo

  • 相关阅读:
    对于EMC DAE、DPE、SPE、SPS的解释
    linux用户添加组
    do_group_exit函数
    bpf移植到3.10
    网络中的GSO分段,整个tcp/ip协议栈中都哪里发生了分段
    发送tcp的时候,数据包是如何拷贝的
    安装llvm
    怎么打印lua的函数调用栈
    调度的log 1.5ms 12ms 4ms
    显示两个文本的差异:强大的grep
  • 原文地址:https://www.cnblogs.com/liuning8023/p/3351600.html
Copyright © 2011-2022 走看看