aboutsummaryrefslogtreecommitdiff
path: root/files/ru/web/javascript/reference/statements/let/index.html
blob: df2a17de02df4d424772994aac642ef9caa45dda (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
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
---
title: let
slug: Web/JavaScript/Reference/Statements/let
translation_of: Web/JavaScript/Reference/Statements/let
---
<div>{{jsSidebar("Statements")}}</div>

<div>Директива <code><strong>let</strong></code> объявляет переменную с блочной областью видимости с возможностью инициализировать её значением.</div>

<div></div>

<h2 id="Синтаксис">Синтаксис</h2>

<pre class="syntaxbox">let var1 [= value1] [, var2 [= value2]] [, ..., varN [= valueN]];
</pre>

<h3 id="Параметры">Параметры</h3>

<dl>
 <dt><code>var1</code>, <code>var2</code>, …, <code>varN</code></dt>
 <dd>Имя переменной. Может использоваться любой допустимый идентификатор.</dd>
 <dt><code>value1</code>, <code>value2</code>, …, <code>valueN</code></dt>
 <dd>Значение переменной. Любое допустимое выражение.</dd>
</dl>

<h2 id="Описание">Описание</h2>

<p>Директива <code><strong>let</strong></code><strong> </strong>позволяет объявить локальную переменную с областью видимости, ограниченной текущим блоком кода . В отличие от ключевого слова <a href="/en-US/docs/JavaScript/Reference/Statements/var" title="JavaScript/Reference/Statements/var"><code>var</code></a>, которое объявляет переменную глобально или локально во всей функции, независимо от области блока.</p>

<p>Объяснение, почему было выбрано название "<strong>let</strong>" можно найти <a href="https://stackoverflow.com/questions/37916940/why-was-the-name-let-chosen-for-block-scoped-variable-declarations-in-javascri">здесь</a>.</p>

<h3 id="Правила_области_видимости_2">Правила области видимости</h3>

<p>Областью видимости переменных, объявленных ключевым словом <code>let</code>, является блок, в котором они объявлены, и все его подблоки. В этом работа директива <code>let</code> схожа с работой директивы <code>var</code>. Основная разница заключается в том, что областью видимости переменной, объявленной директивой <code>var</code>, является вся функция, в которой она объявлена:</p>

<pre class="brush:js">function varTest() {
  var x = 1;
  if (true) {
    var x = 2;  // та же переменная!
    console.log(x);  // 2
  }
  console.log(x);  // 2
}

function letTest() {
  let x = 1;
  if (true) {
    let x = 2;  // другая переменная
    console.log(x);  // 2
  }
  console.log(x);  // 1
}
</pre>

<h3 id="sect1"></h3>

<h3 id="Чище_код_во_вложенных_функциях">Чище код во вложенных функциях</h3>

<p><code>let</code> иногда делает код чище при использовании вложенных функций.</p>

<pre class="brush: js">var list = document.getElementById("list");

for (let i = 1; i &lt;= 5; i++) {
  let item = document.createElement('li');
  item.appendChild(document.createTextNode('Item ' + i));

  item.onclick = function(ev) {
    console.log('Item ' + i + ' is clicked.');
  };
  list.appendChild(item);
}

// чтобы получить такой же эффект с использованием 'var'
// необходимо создать новый контекст
// используя замыкание, чтобы сохранить значение неизменённым
for (var i = 1; i &lt;= 5; i++) {
  var item = document.createElement("li");
  item.appendChild(document.createTextNode("Item " + i));

    (function(i){
        item.onclick = function(ev) {
            console.log('Item ' + i + ' is clicked.');
        };
    })(i);
  list.appendChild(item);
}</pre>

<p>Пример выше будет выполнен как и ожидается, так как пять экземпляров внутренней функции (анонимной) будут ссылаться на пять разных экземпляров переменной <code>i</code>. Пример будет выполнен неверно, если заменить директиву <code>let</code> на <code>var,</code> или удалить переменную <code>i</code> из параметров вложенной функции и использовать внешнюю переменную <code>i</code> во внутренней функции.</p>

<p id="Правила_области_видимости">На верхнем уровне скриптов и функций <code>let, в отличии от var, не создаёт свойства на глобальном объекте</code>. Например:</p>

<pre class="brush:js">var x = 'global_x';
let y = 'global_y';
console.log(this.x); // 'global_x'
console.log(this.y); // undefined
</pre>

<p>В выводе программы будет отображено слово "global_x" для <code>this.x</code>, но <code>undefined</code> для <code>this.y</code>.</p>

<h3 id="Эмуляция_приватных_членов">Эмуляция приватных членов</h3>

<p>При взаимодействии с <a href="https://developer.mozilla.org/en-US/docs/Glossary/Constructor">конструкторами</a> можно использовать выражение <strong><code>let</code></strong> чтобы открыть доступ к одному или нескольким приватным членам через использование <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures">замыканий</a>:</p>

<pre class="brush: js">var SomeConstructor;

{
    let privateScope = {};

    SomeConstructor = function SomeConstructor() {
        this.someProperty = 'foo';
        privateScope.hiddenProperty = 'bar';
    }

    SomeConstructor.prototype.showPublic = function() {
        console.log(this.someProperty); // foo
    }

    SomeConstructor.prototype.showPrivate = function() {
        console.log(privateScope.hiddenProperty); // bar
    }

}

var myInstance = new SomeConstructor();

myInstance.showPublic();
myInstance.showPrivate();

console.log(privateScope.hiddenProperty); // error</pre>

<p>Эта техника позволяет получить только "статичное" приватное состояние - в примере выше, все экземпляры полученные из конструктора <code>SomeConstructor</code> будут ссылаться на одну и ту же область видимости <code>privateScope</code>.</p>

<h3 id="Временные_мёртвые_зоны_и_ошибки_при_использовании_let">Временные мёртвые зоны и ошибки при использовании <code>let</code></h3>

<p>Повторное объявление той же переменной в том же блоке или функции приведёт к выбросу исключения <a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/SyntaxError">SyntaxError</a>.</p>

<pre class="brush: js">if (x) {
  let foo;
  let foo; // SyntaxError thrown.
}</pre>

<p>В стандарте ECMAScript 2015 переменные, объявленные директивой let, переносятся в начало блока. Но если вы сошлётесь в блоке на переменную, до того как она объявлена директивой let, то это приведёт к выбросу исключения <code><a href="/en-US/docs/JavaScript/Reference/Global_Objects/ReferenceError" title="TypeError">ReferenceError</a></code>, потому что переменная находится во "временной мёртвой зоне" с начала блока и до места её объявления. (В отличии от переменной, объявленной через <code>var</code>, которая просто будет содержать значение <code>undefined</code>)</p>

<pre class="brush: js"><code>function do_something() {
  console.log(bar); // undefined
  console.log(foo); // ReferenceError: foo is not defined
  var bar = 1;
  let foo = 2;
}</code></pre>

<p>Вы можете столкнуться с ошибкой в операторах блока  <code><a href="/en-US/docs/JavaScript/Reference/Statements/switch" title="switch">switch</a></code>, так как он имеет только один подблок.</p>

<pre class="brush: js">switch (x) {
  case 0:
    let foo;
    break;

  case 1:
    let foo; // Выброс SyntaxError из-за повторного объявления переменной
    break;
}</pre>

<h3 id="Использование_let_в_циклах_for"><code>Использование let в циклах</code> <code>for</code></h3>

<p>Вы можете использовать ключевое слово <code>let</code> для привязки переменных к локальной области видимости цикла <code>for</code>. Разница с использованием <code>var</code> в заголовке цикла <code>for</code>, заключается в том, что переменные объявленные <code>var</code>, будут видны во всей функции, в которой находится этот цикл.</p>

<pre class="brush:js">var i=0;
for ( let i=i ; i &lt; 10 ; i++ ) {
  console.log(i);
}
</pre>

<h3 id="Правила_области_видимости_3">Правила области видимости</h3>

<pre class="brush: js">for (let <var>expr1</var>; <var>expr2</var>; <var>expr3</var>) <var>statement</var>
</pre>

<p>В этом примере <var>expr2</var>, <var>expr3, statement </var> заключены в неявный блок, который содержит блок локальных переменных, объявленных конструкцией <code>let <em>expr1</em></code>. Пример приведён выше.</p>

<h2 id="Примеры">Примеры</h2>

<h3 id="let_vs_var"><code>let</code> vs <code>var</code></h3>

<p>Когда let используется внутри блока, то область видимости переменной ограничивается этим блоком. Напомним, что отличие заключается в том, что областью видимости переменных, объявленных директивой var, является вся функция, в которой они были объявлены.</p>

<pre class="brush: js">var a = 5;
var b = 10;

if (a === 5) {
  let a = 4; // The scope is inside the if-block
  var b = 1; // The scope is inside the function

  console.log(a);  // 4
  console.log(b);  // 1
}

console.log(a); // 5
console.log(b); // 1</pre>

<h3 id="let_в_циклах"><code>let</code> в циклах</h3>

<p>Вы можете использовать ключевое слово <code>let</code> для привязки переменных к локальной области видимости цикла <code>for</code>, вместо того что бы использовать глобальные переменные (объявленные с помощью <code>var</code>).</p>

<pre class="brush: js">for (let i = 0; i&lt;10; i++) {
  console.log(i); // 0, 1, 2, 3, 4 ... 9
}

console.log(i); // i is not defined</pre>

<h2 id="Нестандартизированные_расширения_let">Нестандартизированные расширения <code>let</code></h2>

<h3 id="let_блок"><code>let</code> блок</h3>

<div class="warning">
<p><code>Поддержка let</code> блоков была убрана в Gecko 44  {{bug(1023609)}}.</p>
</div>

<p><strong>let блок</strong> предоставляет способ, ассоциировать значения с переменными внутри области видимости этого блока, без влияния на значения переменных с теми же именами вне этого блока.</p>

<h4 id="Синтаксис_2">Синтаксис</h4>

<pre class="brush: js">let (var1 [= value1] [, var2 [= value2]] [, ..., varN [= valueN]]) block;
</pre>

<h4 id="Описание_2">Описание</h4>

<p><strong><code>let</code> </strong>блок предоставляет локальную область видимости для переменных. Работа его заключается в привязке нуля или более переменных к области видимости этого блока кода, другими словами, он является блоком операторов. Отметим, что область видимости переменных, объявленных директивой <code>var</code>, в <strong>блоке <code>let</code></strong>, будет той же самой, что и если бы эти переменные были объявлены вне <strong>блока <code>let</code></strong>, иными словами областью видимости таких переменных по-прежнему является функция. Скобки в<strong> блоке </strong><code><strong>let</strong></code> являются обязательными. Опускание их приведёт к синтаксической ошибке.</p>

<h4 id="Пример">Пример</h4>

<pre class="brush:js">var x = 5;
var y = 0;

let (x = x+10, y = 12) {
  console.log(x+y); // 27
}

console.log(x + y); // 5
</pre>

<p>Правила для этого блока кода аналогичны как и для любого другого блока кода в JavaScript. Он может содержать свои локальные переменные, объявленные <code>let</code>.</p>

<h4 id="Правила_области_видимости_4">Правила области видимости</h4>

<p>Областью видимости переменных, объявленных директивой <code>let</code>, в <strong>блоке </strong><code><strong>let</strong></code> является сам блок и все подблоки в нем, если они не содержат объявлений переменных с теми же именами. </p>

<h3 id="let_выражения"><code>let</code> выражения</h3>

<div class="warning">
<p><code>Поддержка let выражений</code> была убрана в Gecko 41  {{bug(1023609)}}.</p>
</div>

<p><strong><code>let выражение</code></strong> позволяет объявить переменные с областью видимости ограниченной одним выражением.</p>

<h4 id="Синтаксис_3">Синтаксис</h4>

<pre class="syntaxbox">let (var1 [= value1] [, var2 [= value2]] [, ..., varN [= valueN]]) expression;</pre>

<h4 id="Пример_2">Пример</h4>

<p>Вы можете использовать let для объявления переменных, областью видимости которых является только одно выражение:</p>

<pre class="brush: js">var a = 5;
let(a = 6) console.log(a); // 6
console.log(a); // 5</pre>

<h4 id="Правила_области_видимости_5">Правила области видимости</h4>

<p>В данном <strong><code>let</code> выражении</strong>:</p>

<pre class="brush: js">let (<var>decls</var>) <var>expr</var>
</pre>

<p><em><code>expr</code> </em>оборачивается в неявный блок.</p>

<h2 id="Спецификации">Спецификации</h2>

<table class="standard-table">
 <tbody>
  <tr>
   <th scope="col">Specification</th>
   <th scope="col">Status</th>
   <th scope="col">Comment</th>
  </tr>
  <tr>
   <td>{{SpecName('ES2015', '#sec-let-and-const-declarations', 'Let and Const Declarations')}}</td>
   <td>{{Spec2('ES2015')}}</td>
   <td>Первоначальное определение. Не описывает let выражения или let блоки.</td>
  </tr>
  <tr>
   <td>{{SpecName('ESDraft', '#sec-let-and-const-declarations', 'Let and Const Declarations')}}</td>
   <td>{{Spec2('ESDraft')}}</td>
   <td></td>
  </tr>
 </tbody>
</table>

<h2 id="Поддержка_браузерами">Поддержка браузерами</h2>

<p>{{CompatibilityTable}}</p>

<div id="compat-desktop">
<table class="compat-table">
 <tbody>
  <tr>
   <th>Feature</th>
   <th>Chrome</th>
   <th>Firefox (Gecko)</th>
   <th>Internet Explorer</th>
   <th>Opera</th>
   <th>Safari</th>
  </tr>
  <tr>
   <td>Basic support</td>
   <td>
    <p class="p1">{{CompatChrome(41.0)}}</p>
   </td>
   <td>{{ CompatGeckoDesktop("1.8.1") }} [1]</td>
   <td>11</td>
   <td>17</td>
   <td>{{CompatUnknown}}</td>
  </tr>
  <tr>
   <td>Temporal dead zone</td>
   <td>{{CompatUnknown}}</td>
   <td>{{ CompatGeckoDesktop("35") }} [1]</td>
   <td>{{CompatUnknown}}</td>
   <td>{{CompatUnknown}}</td>
   <td>{{CompatUnknown}}</td>
  </tr>
  <tr>
   <td><code>let</code> expression {{non-standard_inline}}</td>
   <td>{{CompatNo}}</td>
   <td>{{ CompatGeckoDesktop("1.8.1") }} [1]</td>
   <td>{{CompatNo}}</td>
   <td>{{CompatNo}}</td>
   <td>{{CompatNo}}</td>
  </tr>
  <tr>
   <td><code>let</code> block {{non-standard_inline}}</td>
   <td>{{CompatNo}}</td>
   <td>{{ CompatGeckoDesktop("1.8.1") }} [1]</td>
   <td>{{CompatNo}}</td>
   <td>{{CompatNo}}</td>
   <td>{{CompatNo}}</td>
  </tr>
 </tbody>
</table>
</div>

<div id="compat-mobile">
<table class="compat-table">
 <tbody>
  <tr>
   <th>Feature</th>
   <th>Android</th>
   <th>Chrome for Android</th>
   <th>Firefox Mobile (Gecko)</th>
   <th>IE Mobile</th>
   <th>Opera Mobile</th>
   <th>Safari Mobile</th>
  </tr>
  <tr>
   <td>Basic support</td>
   <td>{{CompatUnknown}}</td>
   <td>
    <p class="p1">{{CompatChrome(41.0)}}</p>
   </td>
   <td>{{ CompatGeckoMobile("1.8.1") }} [1]</td>
   <td>{{CompatUnknown}}</td>
   <td>{{CompatUnknown}}</td>
   <td>{{CompatUnknown}}</td>
  </tr>
  <tr>
   <td>Temporal dead zone</td>
   <td>{{CompatUnknown}}</td>
   <td>{{CompatUnknown}}</td>
   <td>{{ CompatGeckoMobile("35") }} [1]</td>
   <td>{{CompatUnknown}}</td>
   <td>{{CompatUnknown}}</td>
   <td>{{CompatUnknown}}</td>
  </tr>
  <tr>
   <td><code>let</code> expression {{non-standard_inline}}</td>
   <td>{{CompatNo}}</td>
   <td>{{CompatNo}}</td>
   <td>{{ CompatGeckoMobile("1.8.1") }} [1]</td>
   <td>{{CompatNo}}</td>
   <td>{{CompatNo}}</td>
   <td>{{CompatNo}}</td>
  </tr>
  <tr>
   <td><code>let</code> block {{non-standard_inline}}</td>
   <td>{{CompatNo}}</td>
   <td>{{CompatNo}}</td>
   <td>{{ CompatGeckoMobile("1.8.1") }} [1]</td>
   <td>{{CompatNo}}</td>
   <td>{{CompatNo}}</td>
   <td>{{CompatNo}}</td>
  </tr>
 </tbody>
</table>
</div>

<h3 id="sect2"></h3>

<h3 id="Особенности_Firefox">Особенности Firefox</h3>

<ul>
 <li>До SpiderMonkey 46 {{geckoRelease(46)}} выбрасывал {{jsxref("TypeError")}} на повторное объявление, вместо {{jsxref("SyntaxError")}} ({{bug(1198833)}}).</li>
 <li>До SpiderMonkey 44 {{geckoRelease(44)}}<code>let</code> был доступен только для блоков кода обёрнутых в HTML <code>&lt;script type="application/javascript;version=1.7"&gt;</code>block (or higher version) и имел другую семантику.</li>
 <li>Поддержка в {{domxref("Worker")}} код спрятан за <code>dom.workers.latestJSVersion</code> флагом ({{bug(487070)}}). Без версии <code>let</code>, флаг будет удалён в будущем ({{bug(1219523)}}).</li>
 <li>Соблюдение стандарта ES2015 для <code>let</code> в SpIderMonkey отслеживается в {{bug(950547)}}</li>
</ul>