什么时候是JavaScript';这不是邪恶吗?

什么时候是JavaScript';这不是邪恶吗?,javascript,coding-style,eval,Javascript,Coding Style,Eval,我正在编写一些JavaScript代码来解析用户输入的函数(用于类似电子表格的功能)。解析完公式后,我可以将其转换为JavaScript并在其上运行eval(),以生成结果 然而,如果我可以避免使用eval(),我总是回避使用它,因为它是邪恶的(而且,无论正确与否,我一直认为它在JavaScript中更邪恶,因为要评估的代码可能会被用户更改) 那么,什么时候可以使用它?当您信任源时 对于JSON,篡改源代码或多或少是困难的,因为它来自您控制的web服务器。只要JSON本身不包含用户上传的数据,使

我正在编写一些JavaScript代码来解析用户输入的函数(用于类似电子表格的功能)。解析完公式后,我可以将其转换为JavaScript并在其上运行
eval()
,以生成结果

然而,如果我可以避免使用
eval()
,我总是回避使用它,因为它是邪恶的(而且,无论正确与否,我一直认为它在JavaScript中更邪恶,因为要评估的代码可能会被用户更改)


那么,什么时候可以使用它?

当您信任源时

对于JSON,篡改源代码或多或少是困难的,因为它来自您控制的web服务器。只要JSON本身不包含用户上传的数据,使用eval就没有什么大的缺点


在所有其他情况下,在将用户提供的数据提供给eval()之前,我会竭尽全力确保其符合我的规则。

您应该使用eval()的唯一实例是需要动态运行dynamic JS。我说的是从服务器异步下载的JS


…10的9倍可以通过重构轻松避免这样做。

如果您完全控制传递给
eval
函数的代码,就可以使用它。

我倾向于遵循
eval()
,并完全避免它。即使是看起来需要它的方式也不需要。例如,
setTimeout()
允许您传递函数而不是eval

setTimeout(function() {
  alert('hi');
}, 1000);
var a = eval("3 + 5");

即使它是一个可信的源代码,我也不会使用它,因为JSON返回的代码可能是乱码,这充其量只能做一些不可靠的事情,最坏的情况是暴露一些不好的东西。

只有在测试期间,如果可能的话。还要注意,eval()比其他专门的JSON等计算器慢得多。

eval()
并不是坏事。或者,如果是的话,它是邪恶的,就像反射、文件/网络I/O、线程和IPC在其他语言中是邪恶的一样


如果出于您的目的,
eval()
比手动解释更快,或者使您的代码更简单、更清晰。。。那你应该用它。如果两者都不是,那么你就不应该。就这么简单。

没有理由不使用eval(),只要您可以确保代码源来自您或实际用户。尽管他可以操作发送到eval()函数中的内容,但这不是安全问题,因为他可以操作网站的源代码,因此可以更改JavaScript代码本身


所以。。。何时不使用eval()?只有当第三方可能更改Eval()时,才应使用Eval()。比如拦截客户端和服务器之间的连接(但如果这是一个问题,请使用HTTPS)。你不应该因为解析别人在论坛上写的代码而使用eval()。

我想花点时间来回答你问题的前提——eval()是“邪恶的”。编程语言人员使用的“邪恶”一词通常意味着“危险”,或者更准确地说,意味着“用一个简单的命令就能造成很多伤害”。那么,什么时候可以使用危险的东西?当你知道危险是什么,当你采取适当的预防措施时

现在,让我们来看一下使用eval()的危险。与其他事情一样,可能存在许多小的隐患,但两大风险——eval()被视为邪恶的原因——是性能和代码注入

  • Performance-eval()运行解释器/编译器。如果您的代码被编译,那么这是一个很大的打击,因为您需要在运行时调用一个可能重的编译器。但是,JavaScript仍然主要是一种解释性语言,这意味着在一般情况下调用eval()不会对性能造成太大影响(但请参见下面我的具体说明)
  • 代码注入-eval()可能以提升的权限运行一系列代码。例如,以管理员/根用户身份运行的程序永远不会希望eval()用户输入,因为该输入可能是“rm-rf/etc/important file”或更糟的输入。同样,浏览器中的JavaScript也没有这个问题,因为程序无论如何都是在用户自己的帐户中运行的。服务器端JavaScript可能有这个问题
关于你的具体情况。据我所知,您是自己生成字符串的,因此假设您小心地不允许生成“rm-rf something important”这样的字符串,那么就没有代码注入风险(但请记住,在一般情况下很难确保这一点)。另外,如果您在浏览器中运行,那么代码注入是一个相当小的风险,我相信


至于性能,您必须将其与编码的易用性进行权衡。我认为,如果要解析公式,最好在解析过程中计算结果,而不是运行另一个解析器(eval()中的解析器)。但是,使用eval()编写代码可能更容易,而且性能上的影响可能不会明显。在这种情况下,eval()似乎并不比任何其他可能为您节省时间的函数更邪恶。

我看到有人主张不要使用eval,因为它是邪恶的,但我看到同样的人动态使用函数和setTimeout,所以他们在幕后使用eval:D

顺便说一句,如果您的沙箱不够确定(例如,如果您正在一个允许代码注入的站点上工作),那么eval是您的最后一个问题。安全的基本规则是,所有输入都是邪恶的,但在JavaScript中,甚至JavaScript本身也可能是邪恶的,因为在JavaScript中,您可以覆盖任何函数,但无法确定您使用的是真实的函数,因此,如果恶意代码在您之前启动,您不能信任任何JavaScript内置函数:D

现在这篇文章的结尾是:

如果你真的需要它(80%的时间eval是不需要的),并且你确定你在做什么,只需使用eval(或
<html>
    <body>
        <textarea id="output"></textarea><br/>
        <input type="text" id="input" />
        <button id="button" onclick="execute()">eval</button>

        <script type="text/javascript">
            var execute = function(){
                var inputEl = document.getElementById('input');
                var toEval = inputEl.value;
                var outputEl = document.getElementById('output');
                var output = "";

                try {
                    output = eval(toEval);
                }
                catch(err){
                    for(var key in err){
                        output += key + ": " + err[key] + "\r\n";
                    }
                }
                outputEl.value = output;
            }
        </script>
    <body>
</html>
my_func({
    name: "OSM",
    tileURLexpr: '"http://tile.openstreetmap.org/"+b+"/"+a.x+"/"+a.y+".png"',
    ...
});

function my_func(opts)
{
    return new google.maps.ImageMapType({
        getTileUrl: function (coord, zoom) {
            var b = zoom;
            var a = coord;
            return eval(opts.tileURLexpr);
        },
        ....
    });
}
var a = eval("3 + 5");
var f = eval("(function(a,b) { return a + b; })");

var a = f(3,5);
"FirstName + ' ' + LastName"
"LastName + ' ' + FirstName"
var components = require('components');
var Button = components.Button;
var ComboBox = components.ComboBox;
var CheckBox = components.CheckBox;
...
// That quickly gets very boring
var components = require('components');
eval(importable('components', 'Button', 'ComboBox', 'CheckBox', ...));
function importable(path) {
    var name;
    var pkg = eval(path);
    var result = '\n';

    for (name in pkg) {
        result += 'if (name !== undefined) throw "import error: name already exists";\n'.replace(/name/g, name);
    }

    for (name in pkg) {
        result += 'var name = path.name;\n'.replace(/name/g, name).replace('path', path);
    }
    return result;
}
(function () {
    var eval = function (arg) {
    };

    function evalTest() {
        var used = "used";
        var unused = "not used";

        (function () {
            used.toString();   // Variable "unused" is visible in debugger
            eval("1");
        })();
    }

    evalTest();
})();

(function () {
    var eval = function (arg) {
    };

    function evalTest() {
        var used = "used";
        var unused = "not used";

        (function () {
            used.toString();   // Variable "unused" is NOT visible in debugger
            var noval = eval;
            noval("1");
        })();
    }

    evalTest();
})();

(function () {
    var noval = function (arg) {
    };

    function evalTest() {
        var used = "used";
        var unused = "not used";

        (function () {
            used.toString();    // Variable "unused" is NOT visible in debugger
            noval("1");
        })();
    }

    evalTest();
})();
<div>
    {{#each names}}
        <span>{{this}}</span>
    {{/each}}
</div>
(function (state) {
    var Runtime = Hyperbars.Runtime;
    var context = state;
    return h('div', {}, [Runtime.each(context['names'], context, function (context, parent, options) {
        return [h('span', {}, [options['@index'], context])]
    })])
}.bind({}))
const params = { id: 5 };

const route = '/api/user/{id}';
route.replace(/{/g, '${params.');

// use eval(route); to do something