21 грудня 2021 р.

Eval: виконання рядка коду

Вбудована функція eval дозволяє виконувати рядок, як код.

Синтаксис:

let result = eval(code);

Наприклад:

let code = 'alert("Привіт")';
eval(code); // Привіт

Рядок коду може бути великий, містити перехід на інший рядок, оголошення функцій, змінні тощо.

Результатом eval буде результат виконання останньої інструкції.

Наприклад:

let value = eval('1+1');
alert(value); // 2
let value = eval('let i = 0; ++i');
alert(value); // 1

Код eval виконується в поточному лексичному середовищі, тому йому доступні зовнішні змінні:

let a = 1;

function f() {
  let a = 2;

  eval('alert(a)'); // 2
}

f();

Значення зовнішніх змінних можна змінювати:

let x = 5;
eval("x = 10");
alert(x); // 10, значення змінено

У строгому режимі у eval є своє лексичне середовище. Тому функції та змінні, оголошені всередині eval, не можна побачити ззовні:

// нагадування: режим 'use strict' включений за замовчуванням у всіх прикладах, що виконуються

eval("let x = 5; function f() {}");

alert(typeof x); // undefined (немає такої змінної)
// функція f теж недосяжна

Без use strict у eval не буде окремого лексичного середовища, тому x та f будуть видні із зовнішнього коду.

Використання “eval”

У сучасній розробці JavaScript eval використовується дуже рідко. Є навіть відомий вираз – “eval is evil” (“eval – це зло”).

Причина такого ставлення досить проста: давно JavaScript був не дуже розвиненою мовою, і багато речей можна було зробити тільки за допомогою eval. Але та епоха закінчилася понад десять років тому.

На цей час немає жодних причин, щоб продовжувати використовувати eval. Якщо хтось все ще робить це, то дуже ймовірно, що вони легко зможуть замінити eval більш сучасними конструкціями або JavaScript-модулями.

Будь ласка, майте на увазі, що код eval здатний отримувати доступ до зовнішніх змінних, і це може мати побічні ефекти.

Мінімізатори коду (інструменти, що використовуються для стиснення JS-коду перед тим, як надіслати його кінцевим користувачам) замінюють локальні змінні на інші з короткими іменами для оптимізації. Зазвичай це безпечна маніпуляція, але не тоді, коли код використовується eval, бо код з eval може змінювати значення локальних змінних. Тому мінімізатори не чіпають імена змінних, які можуть бути доступні з eval. Це погіршує рівень стиснення коду.

Використання всередині eval локальних змінних із зовнішнього коду вважається поганим рішенням, оскільки це ускладнює завдання підтримки такого коду.

Існує два шляхи, як гарантовано уникнути таких проблем.

Якщо код всередині eval не використовує зовнішніх змінних, то викликайте його так – window.eval(...):

У цьому випадку код виконується у глобальній області видимості:

let x = 1;
{
  let x = 5;
  window.eval('alert(x)'); // 1 (глобальна змінна)
}

Якщо коду всередині eval потрібні локальні змінні, поміняйте eval на new Function та передавайте необхідні дані як аргументи:

let f = new Function('a', 'alert(a)');

f(5); // 5

Конструкція new Function пояснюється у розділі Синтаксис "new Function". Вона створює функцію рядка в глобальній області видимості. Отже, локальні змінні для неї невидимі, але завжди можна передати їх як аргументи. Виходить дуже акуратний код, як у прикладі вище.

Підсумки

Виклик eval(code) виконує рядок коду та повертає результат останньої інструкції.

  • Це рідко використовується в сучасному JavaScript, тому що в цьому нема потреби.
  • Можливий доступ до зовнішніх локальних змінних. Це вважається поганою практикою.
  • Щоб виконати рядок коду за допомогою eval у глобальній області видимості, використовуйте window.eval(code).
  • Або ж, якщо ваш код потребує якихось даних із зовнішньої області видимості, то використовуйте new Function, передавши ці дані як аргументи.

Завдання

важливість: 4

Create a calculator that prompts for an arithmetic expression and returns its result.

There’s no need to check the expression for correctness in this task. Just evaluate and return the result.

Запустити демонстрацію

Let’s use eval to calculate the maths expression:

let expr = prompt("Type an arithmetic expression?", '2*3+2');

alert( eval(expr) );

The user can input any text or code though.

To make things safe, and limit it to arithmetics only, we can check the expr using a regular expression, so that it only may contain digits and operators.

Навчальна карта