diff options
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.html | 323 |
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 > 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> |