aboutsummaryrefslogtreecommitdiff
path: root/files/uk/web/javascript/reference/global_objects/eval/index.html
blob: 8f9fae1236692ef049ed861290da68e6fc6359a9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
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>