Когда eval () в JavaScript не является злом? - PullRequest
242 голосов
/ 13 октября 2008

Я пишу некоторый код JavaScript для анализа введенных пользователем функций (для работы с таблицами). Проанализировав формулу, я может преобразовать ее в JavaScript и запустить eval() для получения результата.

Однако я всегда уклонялся от использования eval(), если я могу избежать этого, потому что это зло (и, правильно или неправильно, я всегда думал, что это еще более зло в JavaScript, потому что код, который нужно оценивать может быть изменен пользователем).

Итак, когда можно использовать его?

Ответы [ 24 ]

2 голосов
/ 13 октября 2008

Можно использовать, если у вас есть полный контроль над кодом, который передается в функцию eval.

1 голос
/ 19 марта 2019

Я думаю, что любые случаи оправданности eval были бы редкостью. Вы, скорее всего, будете использовать его, думая, что это оправдано, чем использовать его, когда оно на самом деле оправдано.

Вопросы безопасности наиболее известны. Но также имейте в виду, что JavaScript использует JIT-компиляцию, и это плохо работает с eval. Eval чем-то напоминает «черный ящик» для компилятора, и JavaScript должен уметь предсказывать код заранее (до некоторой степени), чтобы безопасно и правильно применять оптимизацию производительности и область видимости. В некоторых случаях влияние на производительность может даже повлиять на другой код за пределами eval.

Если вы хотите узнать больше: https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20%26%20closures/ch2.md#eval

1 голос
/ 11 февраля 2009

eval редко является правильным выбором. Несмотря на то, что могут быть многочисленные случаи, когда вы можете выполнить то, что вам нужно, объединяя сценарий и выполняя его на лету, в вашем распоряжении, как правило, гораздо более мощные и поддерживаемые методы: нотация ассоциативного массива (obj["prop"] - это так же, как obj.prop), замыкания, объектно-ориентированные методы, функциональные методы - используйте их вместо этого.

1 голос
/ 26 апреля 2012

Когда eval () в JavaScript не является злом?

Я всегда пытаюсь отговорить от использования eval . Почти всегда доступно более чистое и удобное решение. Eval не требуется даже для анализа JSON . Eval добавляет в ад обслуживания . Недаром его осуждают такие мастера, как Дуглас Крокфорд.

Но я нашел один пример, где должен использоваться :

Когда вам нужно передать выражение.

Например, у меня есть функция, которая создает для меня общий объект google.maps.ImageMapType, но мне нужно сообщить ему рецепт, как он должен создавать URL-адрес плитки из zoom и coord параметры:

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);
        },
        ....
    });
}
1 голос
/ 13 февраля 2018

На стороне сервера eval полезен при работе с внешними сценариями, такими как sql или influenxdb или mongo. Там, где можно выполнить пользовательскую проверку во время выполнения без повторного развертывания ваших служб.

Например, служба достижений со следующими метаданными

{
  "568ff113-abcd-f123-84c5-871fe2007cf0": {
    "msg_enum": "quest/registration",
    "timely": "all_times",
    "scope": [
      "quest/daily-active"
    ],
    "query": "`SELECT COUNT(point) AS valid from \"${userId}/dump/quest/daily-active\" LIMIT 1`",
    "validator": "valid > 0",
    "reward_external": "ewallet",
    "reward_external_payload": "`{\"token\": \"${token}\", \"userId\": \"${userId}\", \"amountIn\": 1, \"conversionType\": \"quest/registration:silver\", \"exchangeProvider\":\"provider/achievement\",\"exchangeType\":\"payment/quest/registration\"}`"
  },
  "efdfb506-1234-abcd-9d4a-7d624c564332": {
    "msg_enum": "quest/daily-active",
    "timely": "daily",
    "scope": [
      "quest/daily-active"
    ],
    "query": "`SELECT COUNT(point) AS valid from \"${userId}/dump/quest/daily-active\" WHERE time >= '${today}' ${ENV.DAILY_OFFSET} LIMIT 1`",
    "validator": "valid > 0",
    "reward_external": "ewallet",
    "reward_external_payload": "`{\"token\": \"${token}\", \"userId\": \"${userId}\", \"amountIn\": 1, \"conversionType\": \"quest/daily-active:silver\", \"exchangeProvider\":\"provider/achievement\",\"exchangeType\":\"payment/quest/daily-active\"}`"
  }
}

Которые затем позволяют,

  • Прямое внедрение объекта / значений через буквенную строку в формате json, полезно для шаблонных текстов

  • Можно использовать в качестве компаратора, скажем, мы устанавливаем правила, как проверять квест или события в CMS

Против этого:

  • Могут быть ошибки в коде и неполадки в сервисе, если они не полностью протестированы.

  • Если хакер может написать скрипт в вашей системе, то вы в значительной степени облажались.

  • Один из способов проверки вашего сценария - хранить хеш своих сценариев в безопасном месте, чтобы вы могли проверить их перед запуском.

1 голос
/ 30 апреля 2013

Мой пример использования eval: import .

Как обычно это делается.

var components = require('components');
var Button = components.Button;
var ComboBox = components.ComboBox;
var CheckBox = components.CheckBox;
...
// That quickly gets very boring

Но с помощью eval и небольшой вспомогательной функции он выглядит намного лучше:

var components = require('components');
eval(importable('components', 'Button', 'ComboBox', 'CheckBox', ...));

importable может выглядеть (эта версия не поддерживает импорт конкретных элементов).

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;
}
1 голос
/ 29 июля 2009

Что касается клиентского скрипта, я думаю, что вопрос безопасности является спорным вопросом. Все, что загружено в браузер, подвергается манипуляциям и должно рассматриваться как таковое. Использование оператора eval () практически исключает риск, когда есть гораздо более простые способы выполнения кода JavaScript и / или манипулирования объектами в DOM, такими как строка URL в вашем браузере.

javascript:alert("hello");

Если кто-то хочет манипулировать своим DOM, я говорю: откажитесь. За безопасность для предотвращения любого типа атаки всегда должна отвечать серверная программа, точка.

С прагматической точки зрения нет смысла использовать 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>
0 голосов
/ 28 ноября 2016

Генерация кода. Недавно я написал библиотеку под названием Hyperbars , которая ликвидирует разрыв между virtual-dom и рулем . Это делается путем синтаксического анализа шаблона руля и преобразования его в hyperscript . Гиперскрипт сначала генерируется в виде строки, а перед его возвратом eval() превращает его в исполняемый код. В этой конкретной ситуации я обнаружил eval() полную противоположность злу.

В основном с

<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({}))

Производительность eval() не является проблемой в подобной ситуации, потому что вам нужно только один раз интерпретировать сгенерированную строку, а затем многократно использовать исполняемый вывод.

Вы можете увидеть, как была достигнута генерация кода, если вам интересно здесь .

0 голосов
/ 13 октября 2008

Только во время тестирования, если это возможно. Также обратите внимание, что eval () намного медленнее, чем другие специализированные оценщики JSON и т. Д.

0 голосов
/ 12 июля 2013

Eval полезен для генерации кода, когда у вас нет макросов.

В (глупом) примере, если вы пишете компилятор Brainfuck , вы, вероятно, захотите создать функцию, которая выполняет последовательность инструкций в виде строки, и вывести ее для возврата функция.

...