aboutsummaryrefslogtreecommitdiff
path: root/files/uk/web/javascript/reference/global_objects/eval/index.html
diff options
context:
space:
mode:
Diffstat (limited to 'files/uk/web/javascript/reference/global_objects/eval/index.html')
-rw-r--r--files/uk/web/javascript/reference/global_objects/eval/index.html323
1 files changed, 323 insertions, 0 deletions
diff --git a/files/uk/web/javascript/reference/global_objects/eval/index.html b/files/uk/web/javascript/reference/global_objects/eval/index.html
new file mode 100644
index 0000000000..8f9fae1236
--- /dev/null
+++ b/files/uk/web/javascript/reference/global_objects/eval/index.html
@@ -0,0 +1,323 @@
+---
+title: eval()
+slug: Web/JavaScript/Reference/Global_Objects/eval
+tags:
+ - JavaScript
+ - eval
+ - Обчислення JavaScript
+translation_of: Web/JavaScript/Reference/Global_Objects/eval
+---
+<div>{{jsSidebar("Objects")}}</div>
+
+<p>Функція <code><strong>eval()</strong></code> обчислює код JavaScript, представлений у вигляді рядка.</p>
+
+<div class="blockIndicator warning">
+<p><strong>Застереження:</strong> Виконання коду JavaScript з текстового рядка - це неймовірний ризик для безпеки. Зловмиснику занадто легко запустити який завгодно код, коли ви використовуєте <code>eval()</code>. Дивіться <a href="#Ніколи_не_використовуйте_eval!">Ніколи не використовуйте eval()!</a> нижче.</p>
+</div>
+
+<div>{{EmbedInteractiveExample("pages/js/globalprops-eval.html")}}</div>
+
+
+
+<h2 id="Синтаксис">Синтаксис</h2>
+
+<pre class="syntaxbox"><code>eval(<em>string</em>)</code></pre>
+
+<h3 id="Параметри">Параметри</h3>
+
+<dl>
+ <dt><code>string</code></dt>
+ <dd>Рядок, що відображає вираз, інструкцію чи послідовність інструкцій JavaScript. Вираз може містити змінні та властивості існуючих об'єктів.</dd>
+</dl>
+
+<h3 id="Значення_що_повертається">Значення, що повертається</h3>
+
+<p>Значення, отримане в результаті обчислення наданого коду. Якщо значення порожнє, повертається {{jsxref("undefined")}}.</p>
+
+<h2 id="Опис">Опис</h2>
+
+<p>Функція <code>eval()</code> є методом глобального об'єкта.</p>
+
+<p>Аргументом функції <code>eval()</code> є рядок. Якщо у рядку представлений вираз, <code>eval()</code> обчислює цей вираз. Якщо у аргументі представлено одну чи більше інструкцій JavaScript, <code>eval()</code> обчислює ці інструкції. Не викликайте <code>eval()</code> для обчислення арифметичного виразу; JavaScript обчислює арифметичні вирази автоматично.</p>
+
+<p>Якщо ви створили арифметичний вираз у вигляді рядка, ви можете скористатись <code>eval()</code>, щоб обчислити його пізніше. Припустимо, ви маєте змінну <code>x</code>. Ви можете відкласти обчислення виразу, що містить <code>x</code>, присвоївши рядкове значення виразу, скажімо, "<code>3 * x + 2</code>", змінній, а потім викликати <code>eval()</code> пізніше у скрипті.</p>
+
+<p>Якщо аргумент <code>eval()</code> не є рядком, <code>eval()</code> повертає аргумент без змін. У наступному прикладі вказано конструктор <code>String</code>, і <code>eval()</code> повертає об'єкт <code>String</code>, а не обчислений рядок.</p>
+
+<pre class="brush:js">eval(new String('2 + 2')); // повертає об'єкт String, який містить "2 + 2"
+eval('2 + 2'); // повертає 4
+</pre>
+
+<p>Ви можете обійти це обмеження загальним методом, використавши <code>toString()</code>.</p>
+
+<pre class="brush:js">var expression = new String('2 + 2');
+eval(expression.toString()); // повертає 4
+</pre>
+
+<p>Якщо ви використовуєте функцію <code>eval</code> <em>опосередковано</em>, викликаючи її через інше посилання, ніж <code>eval</code>, <a href="http://www.ecma-international.org/ecma-262/5.1/#sec-10.4.2">згідно ECMAScript 5</a>, вона працює у глобальній області видимості, а не у локальній. Це означає, для прикладу, що оголошення функції створюють глобальні функції, і що обчислюваний код не має доступу до локальних змінних у області видимості, з якої він викликається.</p>
+
+<pre class="brush:js">function test() {
+ var x = 2, y = 4;
+ console.log(eval('x + y'));  // Прямий виклик, використовує локальну область видимості, результат 6
+ var geval = eval; // еквівалентно виклику eval у глобальній області видимості
+ console.log(geval('x + y')); // Непрямий виклик, використовує глобальну область видимості, викидає ReferenceError, бо `x` undefined
+  (0, eval)('x + y'); // інший приклад непрямого виклику
+}
+</pre>
+
+<h2 id="Ніколи_не_використовуйте_eval!">Ніколи не використовуйте <code>eval</code>!</h2>
+
+<p><code>eval()</code> - небезпечна функція, яка виконує переданий код з привілеями того, хто викликає функцію. Якщо ви запустите <code>eval()</code> з рядком, який міг бути уражений зловмисником, ви можете в результаті запустити шкідливий код на машині користувача з дозволами вашої сторінки / розширення. Ще важливіше, що код третьої сторони бачить область видимості, у якій було запущено <code>eval()</code>, що може призвести до можливих атак, способами, до яких схожий {{jsxref("Global_Objects/Function", "конструктор Function")}} не сприйнятливий.</p>
+
+<p>Також метод <code>eval()</code> повільніший, ніж його альтернативи, оскільки йому доводиться викликати інтерпретатор JS, в той час, як багато інших конструкцій оптимізуються сучасними рушіями JS.</p>
+
+<p>Додатково, сучасні інтерпретатори JavaScript перетворюють код JavaScript на машинний код. Це означає, що будь-яке йменування змінних знищується. Тому будь-яке використання eval змусить переглядач виконувати довгі, затратні пошуки імен змінних, щоб зрозуміти, де ця змінна існує у машинному коді, та присвоїти їй значення. До того ж, до змінної можуть бути внесені нові зміни через <code>eval()</code>, наприклад, зміна типу цієї змінної, змушуючи переглядач переобчислювати весь згенерований машинний код, щоб надолужити зміни. Однак, на щастя, існує дуже гарна альтернатива eval: просто використовуйте <a href="/uk/docs/Web/JavaScript/Reference/Global_Objects/Function">window.Function</a>. Приклад того, як можна перетворити код з використанням шкідливого <code>eval()</code> на код з використанням <code>Function</code><code>()</code>, дивіться нижче.</p>
+
+<p>Поганий код з eval:</p>
+
+<pre class="brush:js">function looseJsonParse(obj){
+ return eval(obj);
+}
+console.log(looseJsonParse(
+ "{a:(4-1), b:function(){}, c:new Date()}"
+))
+</pre>
+
+<p>Покращений код без eval:</p>
+
+<pre class="brush:js">function looseJsonParse(obj){
+    return Function('"use strict";return (' + obj + ')')();
+}
+console.log(looseJsonParse(
+   "{a:(4-1), b:function(){}, c:new Date()}"
+))
+</pre>
+
+<p>Порівнюючи ці два фрагменти коду, можна подумати, що вони працюють однаково, але не забувайте: код з eval набагато повільніший. Зверніть увагу на <code>c: new Date()</code> у обчислюваному об'єкті. У функції без eval об'єкт обчислюється у глобальній області видимості, тому переглядач може спокійно припускати, що <code>Date</code> посилається на <code>window.Date</code>, а не на локальну змінну на ім'я <font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.5);">Date</span></font>. Але у коді, що використовує <code>eval()</code>, переглядач не може цього припускати, бо що, як ваш код виглядає наступним чином:</p>
+
+<pre class="brush:js">function Date(n){
+ return ["Понеділок","Вівторок","Середа","Четвер","П'ятниця","Субота","Неділя"][n%7 || 0];
+}
+function looseJsonParse(obj){
+ return eval(obj);
+}
+console.log(looseJsonParse(
+ "{a:(4-1), b:function(){}, c:new Date()}"
+))
+</pre>
+
+<p>Таким чином, у версії з <code>eval()</code> переглядач змушений запускати затратний пошук, щоб перевірити, чи немає десь локальних змінних на ім'я <code>Date()</code>. Це страшенно неефективно, у порівнянні з <code>Function()</code>.</p>
+
+<p>У цих обставинах, що як вам дійсно потрібно, щоб функція <code>Date</code> могла викликатися з коду всередині <code>Function()</code>? Чи доведеться відступити та повернутися до <code>eval()</code>? Ні! Нізащо. Натомість, спробуйте наступний підхід.</p>
+
+<pre class="brush:js">function Date(n){
+ return ["Понеділок","Вівторок","Середа","Четвер","П'ятниця","Субота","Неділя"][n%7 || 0];
+}
+function runCodeWithDateFunction(obj){
+ return Function('"use strict";return (' + obj + ')')()(
+ Date
+  );
+}
+console.log(runCodeWithDateFunction(
+ "function(Date){ return Date(5) }"
+))
+</pre>
+
+<p>Наведений код може виглядати неефективно повільним через потрійну вкладеність функції, але проаналізуємо переваги наведеного вище методу:</p>
+
+<ul>
+ <li>Він дозволяє мініфікувати код у рядку, переданому до <code>runCodeWithDateFunction</code>.</li>
+ <li>Затрати на виклик функції мінімальні, що разом з набагато меншим за розміром кодом є однозначною перевагою</li>
+ <li><code>Function()</code> легше дозволяє покращити продуктивність вашого коду через <code>"use strict";</code></li>
+ <li>Код не використовує <code>eval()</code>, що робить його на порядки швидшим.</li>
+</ul>
+
+<p>І нарешті, розглянемо мініфікацію. Використовуючи <code>Function()</code>, як це показано вище, ви можете мініфікувати рядок коду, що передається у <code>runCodeWithDateFunction</code> набагато ефективніше, тому що імена аргументів функції також можуть бути мініфіковані, як показано у мініфікованому коді нижче.</p>
+
+<pre class="brush:js">console.log(Function('"use strict";return(function(a){return a(5)})')()(function(a){
+return"Понеділок Вівторок Середа Четвер П'ятниця Субота Неділя".split(" ")[a%7||0]}));</pre>
+
+<p>Існують також інші безпечніші (та швидші!) альтернативи <code>eval()</code> чи <code>Function()</code> для типових випадків використання.</p>
+
+<h3 id="Звернення_до_властивостей">Звернення до властивостей</h3>
+
+<p>Не слід використовувати <code>eval()</code> для перетворення імен властивостей на властивості. Розглянемо наступний приклад, де властивість об'єкта, до якої звертаються, невідома до початку виконання коду. Це можна зробити через eval:</p>
+
+<pre class="brush:js">var obj = { a: 20, b: 30 };
+var propName = getPropName(); // повертає "a" або "b"
+
+eval( 'var result = obj.' + propName );
+</pre>
+
+<p>Однак, метод <code>eval()</code> тут не обов'язковий. Насправді, його використання тут не рекомендоване. Натомість, скористайтесь <a href="/uk/docs/Web/JavaScript/Reference/Operators/Property_Accessors" title="JavaScript/Reference/Operators/Member_Operators">зверненням до властивостей</a>, це набагато швидше та безпечніше:</p>
+
+<pre class="brush:js">var obj = { a: 20, b: 30 };
+var propName = getPropName(); // повертає "a" або "b"
+var result = obj[ propName ]; // obj[ "a" ] - це те саме, що й obj.a</pre>
+
+<p>Ви навіть можете скористатись цим методом, щоб звернутись до вкладених властивостей. З <code>eval()</code> це виглядало б наступним чином:</p>
+
+<pre class="brush:js">var obj = {a: {b: {c: 0}}};
+var propPath = getPropPath(); // повертає, наприклад, "a.b.c"
+
+eval( 'var result = obj.' + propPath );
+</pre>
+
+<p>Уникнути <code>eval()</code> тут можна, розбивши шлях до властивості на масив та пройшовши у циклі через властивості:</p>
+
+<pre class="brush:js">function getDescendantProp(obj, desc) {
+  var arr = desc.split('.');
+  while (arr.length) {
+ obj = obj[arr.shift()];
+ }
+  return obj;
+}
+
+var obj = {a: {b: {c: 0}}};
+var propPath = getPropPath(); // повертає, наприклад, "a.b.c"
+var result = getDescendantProp(obj, propPath);</pre>
+
+<p>Призначення властивості таким чином працює схоже:</p>
+
+<pre class="brush:js">function setDescendantProp(obj, desc, value) {
+  var arr = desc.split('.');
+  while (arr.length &gt; 1) {
+ obj = obj[arr.shift()];
+ }
+  return obj[arr[0]] = value;
+}
+
+var obj = {a: {b: {c: 0}}};
+var propPath = getPropPath(); // повертає, наприклад, "a.b.c"
+var result = setDescendantProp(obj, propPath, 1); // obj.a.b.c тепер дорівнюватиме 1</pre>
+
+<h3 id="Використовуйте_функції_замість_обчислення_фрагментів_коду">Використовуйте функції замість обчислення фрагментів коду</h3>
+
+<p>JavaScript має <a class="external" href="https://uk.wikipedia.org/wiki/%D0%A4%D1%83%D0%BD%D0%BA%D1%86%D1%96%D1%8F_%D0%BF%D0%B5%D1%80%D1%88%D0%BE%D0%B3%D0%BE_%D0%BA%D0%BB%D0%B0%D1%81%D1%83" title="https://uk.wikipedia.org/wiki/Функція_першого_класу">функції першого класу</a>, це означає, що ви можете передавати функції як аргументи до інших API, зберігати їх у змінних та властивостях об'єктів, і так далі. Багато API об'єктів DOM створені з метою такого використання, тому ви можете (і маєте) написати:</p>
+
+<pre class="brush: js">// замість setTimeout(" ... ", 1000) використовуйте:
+setTimeout(function() { ... }, 1000);
+
+// замість elt.setAttribute("onclick", "...") використовуйте:
+elt.addEventListener('click', function() { ... } , false); </pre>
+
+<p><a href="/uk/docs/Web/JavaScript/Closures" title="JavaScript/Guide/Closures">Замикання</a> також корисні як спосіб створення параметризованих функцій без поєднання рядків.</p>
+
+<h3 id="Розбір_JSON_перетворення_рядків_на_обєкти_JavaScript">Розбір JSON (перетворення рядків на об'єкти JavaScript)</h3>
+
+<p>Якщо рядок, для якого ви викликаєте <code>eval()</code>, містить дані (наприклад, масив: <code>"[1, 2, 3]"</code>), як протилежність коду, вам слід розглянути перехід на <a href="/uk/docs/Glossary/JSON" title="JSON">JSON</a>, це дозволить рядку використовувати підмножину синтаксису JavaScript для представлення даних. Дивіться також <a href="/uk/docs/Downloading_JSON_and_JavaScript_in_extensions" title="Downloading_JSON_and_JavaScript_in_extensions">Завантаження JSON та JavaScript у розширеннях</a>.</p>
+
+<p>Зауважте, що, оскільки синтаксис JSON є обмеженим, у порівнянні з синтаксисом JavaScript, багато з чинних літералів JavaScript не розбиратимуться як JSON. Наприклад, прикінцеві коми не дозволені у JSON, а імена властивостей (ключі) у об'єкті повинні бути заключені у лапки. Обов'язково використовуйте серіалізатор JSON для створення рядків, які пізніше будуть розібрані як JSON.</p>
+
+<h3 id="Передавайте_дані_замість_коду">Передавайте дані замість коду</h3>
+
+<p>Наприклад, розширення, створене для збирання вмісту веб-сторінок, може мати правила збирання, визначені у <a href="/uk/docs/Web/XPath" title="XPath">XPath</a>, замість коду JavaScript.</p>
+
+<h3 id="Запускайте_код_з_обмеженими_привілеями">Запускайте код з обмеженими привілеями</h3>
+
+<p>Якщо ви мусите запускати код, розгляньте варіант запуску з обмеженими привілеями. Ця порада стосується здебільшого розширень та XUL-застосунків, які можуть використовувати для цього <a href="/uk/docs/Mozilla/Tech/XPCOM/Language_Bindings/Components.utils.evalInSandbox" title="Components.utils.evalInSandbox">Components.utils.evalInSandbox</a>.</p>
+
+<h2 id="Приклади">Приклади</h2>
+
+<h3 id="Використання_eval">Використання <code>eval</code></h3>
+
+<p>У наступному коді обидві інструкції, які містять <code>eval()</code>, повертають 42. Перша обчислює рядок "<code>x + y + 1</code>"; друга обчислює рядок "<code>42</code>".</p>
+
+<pre class="brush:js">var x = 2;
+var y = 39;
+var z = '42';
+eval('x + y + 1'); // повертає 42
+eval(z); // повертає 42
+</pre>
+
+<h3 id="Використання_eval_для_обчислення_рядка_інструкцій_JavaScript">Використання <code>eval</code> для обчислення рядка інструкцій JavaScript</h3>
+
+<p>Наступний приклад використовує <code>eval()</code>, щоб обчислити рядок <code>str</code>. Цей рядок складається з інструкцій JavaScript, які виводять повідомлення та присвоюють змінній <code>z</code> значення 42, якщо <code>x</code> дорівнює п'яти, інакше присвоюють <code>z</code> значення 0. Коли виконується друга інструкція, <code>eval()</code> спричинить виконання цих інструкцій, а також обчислить набір інструкцій та поверне значення, що було присвоєне <code>z</code>.</p>
+
+<pre class="brush:js">var x = 5;
+var str = "if (x == 5) {console.log('z дорівнює 42'); z = 42;} else z = 0;";
+
+console.log('z дорівнює ', eval(str));</pre>
+
+<p>Якщо ви визначаєте декілька значень, то повертається останнє.</p>
+
+<pre class="brush:js">var x = 5;
+var str = "if (x == 5) {console.log('z дорівнює 42'); z = 42; x = 420; } else z = 0;";
+
+console.log('x дорівнює ', eval(str)); // z дорівнює 42 x дорівнює 420
+</pre>
+
+<h3 id="Останній_вираз_обчислюється">Останній вираз обчислюється</h3>
+
+<p><code>eval()</code> повертає значення останнього обчисленого виразу.</p>
+
+<pre class="brush:js">var str = 'if ( a ) { 1 + 1; } else { 1 + 2; }';
+var a = true;
+var b = eval(str); // повертає 2
+
+console.log('b дорівнює : ' + b);
+
+a = false;
+b = eval(str); // повертає 3
+
+console.log('b дорівнює : ' + b);</pre>
+
+<h3 id="eval_як_функція_визначення_рядка_потребує_та_на_початку_та_в_кінці"><code>eval</code> як функція визначення рядка, потребує "(" та ")" на початку та в кінці</h3>
+
+<pre class="brush:js">var fctStr1 = 'function a() {}'
+var fctStr2 = '(function a() {})'
+var fct1 = eval(fctStr1) // повертає undefined
+var fct2 = eval(fctStr2) // повертає функцію
+</pre>
+
+<h2 id="Специфікації">Специфікації</h2>
+
+<table class="standard-table">
+ <tbody>
+ <tr>
+ <th scope="col">Специфікація</th>
+ <th scope="col">Статус</th>
+ <th scope="col">Коментар</th>
+ </tr>
+ <tr>
+ <td>{{SpecName('ES1')}}</td>
+ <td>{{Spec2('ES1')}}</td>
+ <td>Початкове визначення.</td>
+ </tr>
+ <tr>
+ <td>{{SpecName('ES5.1', '#sec-15.1.2.1', 'eval')}}</td>
+ <td>{{Spec2('ES5.1')}}</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>{{SpecName('ES6', '#sec-eval-x', 'eval')}}</td>
+ <td>{{Spec2('ES6')}}</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>{{SpecName('ESDraft', '#sec-eval-x', 'eval')}}</td>
+ <td>{{Spec2('ESDraft')}}</td>
+ <td></td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="Сумісність_з_веб-переглядачами">Сумісність з веб-переглядачами</h2>
+
+
+
+<p>{{Compat("javascript.builtins.eval")}}</p>
+
+<h2 id="Примітки_щодо_Firefox">Примітки щодо Firefox</h2>
+
+<ul>
+ <li>Історично <code>eval()</code> мав необов'язковий другий аргумент, що вказував об'єкт, в контексті якого мало виконуватись обчислення. Цей аргумент був нестандартним, і був остаточно прибраний з Firefox 4. Дивіться {{bug(531675)}}.</li>
+</ul>
+
+<h2 id="Див._також">Див. також</h2>
+
+<ul>
+ <li>{{jsxref("Global_Objects/uneval", "uneval()")}}</li>
+ <li><a href="/uk/docs/Web/JavaScript/Reference/Operators/Property_Accessors">Property accessors</a></li>
+ <li><a href="/uk/Add-ons/WebExtensions/Content_scripts#Using_eval()_in_content_scripts">WebExtensions: Using eval in content scripts</a></li>
+</ul>