# Запуск кода из строки: eval Функция `eval(code)` позволяет выполнить код, переданный ей в виде строки. Этот код будет выполнен в *текущей области видимости*. [cut] ## Использование eval В простейшем случае `eval` всего лишь выполняет код, например: ```js //+ run no-beautify var a = 1; (function() { var a = 2; *!* eval(' alert(a) '); // 2 */!* })() ``` Но он может не только выполнить код, но и вернуть результат. **Вызов `eval` возвращает последнее вычисленное выражение**: Например: ```js //+ run alert( eval('1+1') ); // 2 ``` **При вызове `eval` имеет полный доступ к локальным переменным.** Это означает, что текущие переменные могут быть изменены или дополнены: ```js //+ untrusted refresh run var x = 5; eval(" alert( x ); x = 10"); // 5, доступ к старому значению alert( x ); // 10, значение изменено внутри eval ``` [smart header="В строгом режиме `eval` имеет свою область видимости "] В строгом режиме функционал `eval` чуть-чуть меняется. При `use strict` код внутри `eval` по-прежнему сможет читать и менять внешние переменные, однако переменные и функции, объявленные внутри `eval`, не попадут наружу. ```js //+ untrusted refresh run "use strict"; *!* eval("var a = 5; function f() { }"); */!* alert( a ); // ошибка, переменная не определена // функция f тоже не видна снаружи ``` Иными словами, в новом стандарте `eval` имеет свою область видимости, а к внешним переменным обращается через замыкание, аналогично тому, как работают обычные функции. [/smart] ## Неграмотное использование eval Начнём с того, что `eval` применяется очень редко. Действительно редко. Есть даже такое выражение "eval is evil" (eval -- зло). Причина проста: когда-то JavaScript был гораздо более слабым языком, чем сейчас, и некоторые вещи без `eval` было сделать невозможно. Но те времена давно прошли. И теперь найти тот случай, когда действительно надо выполнить код из строки -- это надо постараться. Но если вы действительно знаете, что это именно тот случай и вам необходим `eval` -- есть ряд вещей, которые нужно иметь в виду. Доступ к локальным переменным -- худшее, что можно сделать при `eval`. Дело в том, что локальные переменные могут быть легко переименованы: ```js function sayHi() { var phrase = "Привет"; eval(str); } ``` Переменная `phrase` может быть переименована в `hello`, и если строка `str` обращается к ней -- будет ошибка. Современные средства сжатия JavaScript переименовывают локальные переменные автоматически. Это считается безопасным, так как локальная переменная видна лишь внутри функции и если в ней везде поменять `phrase` на `p`, то никто этого не заметит. До сжатия: ```js function sayHi() { var phrase = "Привет"; alert( phrase ); } ``` После сжатия: ```js function sayHi() { var a = "Привет"; alert( a ); } ``` На самом деле всё ещё проще -- в данном случае утилита сжатия автоматически уберёт переменную `a` и код станет таким: ```js function sayHi() { alert( "Привет" ); } ``` Итак, если где-то в функции есть `eval`, то его взаимодействие с локальными переменными будет нарушено с непредсказуемыми побочными эффектами. Некоторые инструменты сжатия предупреждают, когда видят `eval` или стараются вообще не сжимать такой код вместе с его внешними функциями, но всё это борьба с последствиями кривого кода. Как правило, `eval` не нужен, именно поэтому говорят, "eval is evil". ## Запуск скрипта в глобальной области Ок, взаимодействовать с локальными переменными нельзя. Но допустим мы загрузили с сервера или вручную сгенерировали скрипт, который нужно выполнить. Желательно, в глобальной области, вне любых функций, чтобы он уж точно к локальным переменным отношения не имел. Здесь `eval` может пригодиться. Есть два трюка для выполнения кода в глобальной области:
  1. Везде, кроме IE8-, достаточно вызвать `eval` не напрямую, а через `window.eval`. Вот так: ```js //+ run no-beautify var a = 1; (function() { var a = 2; *!* window.eval(' alert(a) '); // 1, выполнено глобально везде, кроме IE8- */!* })(); ```
  2. В IE8- можно применить нестандартную фунцию [execScript](http://msdn.microsoft.com/en-us/library/ie/ms536420%28v=vs.85%29.aspx). Она, как и `eval`, выполняет код, но всегда в глобальной области видимости и не возвращает значение.
Оба способа можно объединить в единой функции `globalEval(code)`, выполняющей код без доступа к локальным переменным: ```js //+ run no-beautify *!* function globalEval(code) { // объединим два способа в одну функцию window.execScript ? execScript(code) : window.eval(code); } */!* var a = 1; (function() { var a = 2; globalEval(' alert(a) '); // 1, во всех браузерах })(); ``` ## Внешние данные через new Function Итак, у нас есть код, который, всё же, нужно выполнить динамически, через `eval`, но не просто скрипт -- а ему нужно передать какие-то значения. Как мы говорили ранее, считать их из локальных переменных нельзя: это подвержено ошибкам при переименовании переменных и сразу ломается при сжатии JavaScript. Да и вообще, неочевидно и криво. **К счастью, существует отличная альтернатива `eval`, которая позволяет корректно взаимодействовать c внешним кодом: `new Function`.** Вызов `new Function('a,b', '..тело..')` создает функцию с указанными аргументами `a,b` и телом. Как мы помним, доступа к текущему замыканию у такой функции не будет, но можно передать параметры и получить результат. Например: ```js //+ run var a = 2, b = 3; *!* // вместо обращения к a,b через eval // будем принимать их как аргументы динамически созданной функции var mul = new Function('a, b', ' return a * b;'); */!* alert( mul(a, b) ); // 6 ``` ## JSON и eval В браузерах IE7- не было методов `JSON.stringify` и `JSON.parse`, поэтому работа с JSON происходила через `eval`. Этот способ работы с JSON давно устарел, но его можно встретить кое-где в старом коде, так что для примера рассмотрим его. Вызов `eval(code)` выполняет код и, если это выражение, то возвращает его значение, поэтому можно в качестве кода передать JSON. Например: ```js //+ run var str = '{ \ "name": "Вася", \ "age": 25 \ }'; *!* var user = eval('(' + str + ')'); */!* alert( user.name ); // Вася ``` Зачем здесь нужны скобки `eval( '(' + str + ')' )`, почему не просто `eval(str)`? ...Всё дело в том, что в JavaScript с фигурной скобки `{` начинаются не только объекты, а в том числе и "блоки кода". Что имеется в виду в данном случае -- интерпретатор определяет по контексту. Если в основном потоке кода -- то блок, если в контексте выражения, то объект. Поэтому если передать в `eval` объект напрямую, то интерпретатор подумает, что это на самом деле блок кода, а там внутри какие-то двоеточия... Вот, для примера, `eval` без скобок, он выдаст ошибку: ```js //+ run var user = eval('{ "name": "Вася", "age": 25 }'); ``` А если `eval` получает выражение в скобках `( ... )`, то интерпретатор точно знает, что это не блок кода, а объект: ```js //+ run var user = eval('( { "name": "Вася", "age": 25 } )'); alert( user.age ); // 25 ``` [warn header="Осторожно, злой JSON!"] Если мы получаем JSON из недоверенного источника, например с чужого сервера, то разбор через `eval` может быть опасен. Например, чужой сервер может быть взломан (за свой-то код мы отвечаем, а за чужой -- нет) и вместо JSON вставлен злонамеренный JavaScript-код. **Поэтому рекомендуется, всё же, использовать `JSON.parse`.** При разборе через `JSON.parse` некорректный JSON просто приведёт к ошибке, а вот при разборе через `eval` этот код реально выполнится, он может вывести что-то на странице, перенаправить посетителя куда-то и т.п. [/warn] ## Итого Ещё примеры использования `eval` вы найдёте далее, в главе [](/json).