aboutsummaryrefslogtreecommitdiff
path: root/files/ru/web/javascript/guide/functions/index.html
blob: 28385a4c3e5bd16da6ccdead22de32b86d89ceb9 (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
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
---
title: Функции
slug: Web/JavaScript/Guide/Functions
tags:
  - Основы
  - Функции
translation_of: Web/JavaScript/Guide/Functions
---
<div>{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Loops_and_iteration", "Web/JavaScript/Guide/Expressions_and_Operators")}}</div>

<h2 id="Функции_в_JavaScript">Функции в JavaScript</h2>

<p>Функции - ключевая концепция в JavaScript. Важнейшей особенностью языка является {{interwiki("wikipedia", "Функции_первого_класса", "первоклассная поддержка функций")}}<em>(functions as first-class citizen)</em>. Любая <strong>функция это объект,</strong> и следовательно ею можно манипулировать как объектом, в частности:</p>

<ul>
 <li>передавать как аргумент и возвращать в качестве результата при вызове других функций ({{interwiki("wikipedia", "Функция_высшего_порядка", "функций высшего порядка")}});</li>
 <li>создавать анонимно и присваивать в качестве значений переменных или свойств объектов.</li>
</ul>

<p>Это определяет высокую выразительную мощность JavaScript и позволяет относить его к числу языков, реализующих {{interwiki("wikipedia", "Функциональное_программирование", "функциональную парадигму программирования")}} (что само по себе есть очень круто по многим соображениям).</p>

<p><strong>Функция в JavaScript</strong> специальный тип объектов, позволяющий формализовать средствами языка определённую логику поведения и обработки данных.</p>

<p>Для понимания работы функций необходимо (и достаточно?) иметь представление о следующих моментах:</p>

<ul>
 <li>способы <a href="#definition">объявления</a></li>
 <li>способы <a href="#call">вызова</a></li>
 <li>параметры и аргументы вызова (<code>arguments</code>)</li>
 <li>область данных (<code>Scope</code>) и замыкания (<code>Closures</code>)</li>
 <li>объект привязки (<code>this</code>)</li>
 <li>возвращаемое значение (<code>return</code>)</li>
 <li>исключения (<code>throw</code>)</li>
 <li>использование в качестве конструктора объектов</li>
 <li>сборщик мусора (<code>garbage collector</code>)</li>
</ul>

<h2 id="Объявление_функций"><a id="definition" name="definition"></a>Объявление функций</h2>

<h3 id="Функции_вида_function_declaration_statement">Функции вида "function declaration statement"</h3>

<p>Объявление функции (<em>function definition</em>, или <em>function declaration</em>, или <em>function statement</em>) состоит из ключевого слова <code><a href="/ru/docs/Web/JavaScript/Reference/Statements/function">function</a></code> и следующих частей:</p>

<ul>
 <li>Имя функции.</li>
 <li>Список параметров (принимаемых функцией) заключённых в круглые скобки <code>()</code> и разделённых запятыми.</li>
 <li>Инструкции, которые будут выполнены после вызова функции, заключают в фигурные скобки <code>{ }</code>.</li>
</ul>

<p>Например, следующий код объявляет простую функцию с именем <code>square:</code></p>

<pre class="brush: js">function square(number) {
  return number * number;
}
</pre>

<p>Функция <code>square</code> принимает один параметр, названный <code>number.</code> Состоит из одной инструкции, которая означает вернуть параметр этой функции (это <code>number</code>) умноженный на самого себя. Инструкция <a href="/ru/docs/Web/JavaScript/Reference/Statements/return">return</a> указывает на значение, которые будет возвращено функцией.</p>

<pre class="brush: js">return number * number;
</pre>

<p>Примитивные параметры (например, число) передаются функции значением; <strong>значение</strong> передаётся в функцию, но если функция меняет значение параметра,<strong> это изменение не отразится глобально или после вызова функции.</strong></p>

<p>Если вы передадите объект как параметр (не примитив, например, <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array" title="The JavaScript Array object is a global object that is used in the construction of arrays; which are high-level, list-like objects.">массив</a></code> или определяемые пользователем объекты), и функция изменит свойство переданного в неё объекта, это изменение будет видно и вне функции, как показано в следующим примере:</p>

<pre class="brush: js">function myFunc(theObject) {
  theObject.make = 'Toyota';
}

var mycar = {make: 'Honda', model: 'Accord', year: 1998};
var x, y;

x = mycar.make; // x получает значение "Honda"

myFunc(mycar);
y = mycar.make; // y получает значение "Toyota"
                // (свойство было изменено функцией)
</pre>

<h3 id="Функции_вида_function_definition_expression">Функции вида "function definition expression"</h3>

<p>Функция вида "function declaration statement" по синтаксису является инструкцией (<em>statement</em>), ещё функция может быть вида "function definition  expression". Такая функция может быть <strong>анонимной</strong> (она не имеет имени). Например, функция <code>square</code> может быть вызвана так:</p>

<pre class="brush: js">var square = function(number) { return number * number; };
var x = square(4); // x получает значение 16
</pre>

<p>Однако, имя может быть и присвоено для вызова самой себя внутри самой функции и для отладчика (<em>debugger</em>) для идентифицированные функции в стек-треках (<em>stack traces</em>; "trace" — "след" / "отпечаток").</p>

<pre class="brush: js">var factorial = function fac(n) { return n &lt; 2 ? 1 : n * fac(n - 1); };

console.log(factorial(3));
</pre>

<p>Функции вида "function definition expression" удобны, когда функция передаётся аргументом другой функции. Следующий пример показывает функцию <code>map</code>, которая должна получить функцию первым аргументом и массив вторым.</p>

<pre class="brush: js">function map(f, a) {
  var result = [], // Create a new Array
      i;
  for (i = 0; i != a.length; i++)
    result[i] = f(a[i]);
  return result;
}
</pre>

<p>В следующим коде наша функция принимает функцию, которая является function definition expression, и выполняет его для каждого элемента принятого массива вторым аргументом.</p>

<pre class="brush: js">function map(f, a) {
  var result = []; // Create a new Array
  var i; // Declare variable
  for (i = 0; i != a.length; i++)
    result[i] = f(a[i]);
      return result;
}
var f = function(x) {
   return x * x * x;
}
var numbers = [0, 1, 2, 5, 10];
var cube = map(f,numbers);
console.log(cube);</pre>

<p>Функция возвращает: [0, 1, 8, 125, 1000].</p>

<p>В JavaScript функция может быть объявлена с условием. Например, следующая функция будет присвоена переменной <code>myFunc</code> только, если <code>num</code> равно 0: </p>

<pre class="brush: js">var myFunc;
if (num === 0) {
  myFunc = function(theObject) {
    theObject.make = 'Toyota';
  }
}</pre>

<p>В дополнение к объявлениям функций, описанных здесь, вы также можете использовать конструктор <a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/Function">Function</a> для создания функций из строки во время выполнения (<em>runtime</em>), подобно <a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/eval" title="The eval() method evaluates JavaScript code represented as a string."><code>eval()</code></a>.</p>

<p><strong>Метод</strong> — это функция, которая является свойством объекта. Узнать больше про объекты и методы можно по ссылке: <a href="/ru/docs/Web/JavaScript/Guide/Working_with_Objects">Работа с объектами</a>.</p>

<h2 id="Вызовы_функций"><a id="call" name="call"></a>Вызовы функций</h2>

<p>Объявление функции не выполняет её. Объявление функции просто называет функцию и указывает, что делать при вызове функции.</p>

<p><strong>Вызов</strong> функции фактически выполняет указанные действия с указанными параметрами. Например, если вы определите функцию <code>square</code>, вы можете вызвать её следующим образом:</p>

<pre class="brush: js">square(5);</pre>

<p>Эта инструкция вызывает функцию с аргументом 5. Функция вызывает свои инструкции и возвращает значение 25.</p>

<p>Функции могут быть в области видимости, когда они уже определены, но функции вида "function declaration statment" могут быть подняты (<em><a href="/ru/docs/%D0%A1%D0%BB%D0%BE%D0%B2%D0%B0%D1%80%D1%8C/%D0%9F%D0%BE%D0%B4%D0%BD%D1%8F%D1%82%D0%B8%D0%B5">поднятие </a></em> <em>hoisting</em>), также как в этом примере:</p>

<pre class="brush: js">console.log(square(5));
/* ... */
function square(n) { return n * n; }</pre>

<p>Область видимости функции — функция, в котором она определена, или целая программа, если она объявлена по уровню выше.</p>

<div class="note">
<p><strong>Примечание:</strong> Это работает только тогда, когда объявлении функции использует вышеупомянутый синтаксис (т.е. <code>function funcName(){}</code>). Код ниже не будет работать. Имеется в виду то, что поднятие функции работает только с function declaration и не работает с function expression.</p>
</div>

<pre class="brush: js example-bad">console.log(square); // square поднят со значением undefined.
console.log(square(5)); // TypeError: square is not a function
var square = function(n) {
  return n * n;
}</pre>

<p>Аргументы функции не ограничиваются строками и числами. Вы можете передавать целые объекты в функцию. Функция <code>show_props()</code> (объявленная в <a href="/ru/docs/Web/JavaScript/Guide/Working_with_Objects">Работа с объектами</a>) является примером функции, принимающей объекты аргументом.</p>

<p>Функция может вызвать саму себя. Например, вот функция рекурсивного вычисления факториала:</p>

<pre class="brush: js">function factorial(n) {
  if ((n === 0) || (n === 1))
    return 1;
  else
    return (n * factorial(n - 1));
}
</pre>

<p>Затем вы можете вычислить факториалы от одного до пяти следующим образом:</p>

<pre class="brush: js">var a, b, c, d, e;
a = factorial(1); // a gets the value 1
b = factorial(2); // b gets the value 2
c = factorial(3); // c gets the value 6
d = factorial(4); // d gets the value 24
e = factorial(5); // e gets the value 120
</pre>

<p>Есть другие способы вызвать функцию. Существуют частые случаи, когда функции необходимо вызывать динамически, или поменять номера аргументов функции, или необходимо вызвать функцию с привязкой к определённому контексту. Оказывается, что функции сами по себе являются объектами, и эти объекты в свою очередь имеют методы (посмотрите объект <a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Function" title="The Function constructor creates a new Function object. Calling the constructor directly can create functions dynamically, but suffers from security and similar (but far less significant) performance issues similar to eval. However, unlike eval, the Function constructor allows executing code in the global scope, prompting better programming habits and allowing for more efficient code minification."><code>Function</code></a>). Один из них это метод <code><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Function/apply" title="The apply() method calls a function with a given this value, and arguments provided as an array (or an array-like object).">apply()</a></code>, использование которого может достигнуть этой цели.</p>

<h2 id="Область_видимости_функций"><a id="definition" name="definition"></a>Область видимости функций</h2>

<p><u><em>(function scope)</em></u></p>

<p>Переменные объявленные в функции не могут быть доступными где-нибудь вне этой функции, поэтому переменные (которые нужны именно для функции) объявляют только в scope функции. При этом функция имеет доступ ко всем переменным и функциям, объявленным внутри её scope. Другими словами функция объявленная в глобальном scope имеет доступ ко всем переменным в глобальном scope. Функция объявленная внутри другой функции ещё имеет доступ и ко всем переменным её родительской функции и другим переменным, к которым эта родительская функция имеет доступ.</p>

<pre class="brush: js">// Следующие переменные объявленны в глобальном scope
var num1 = 20,
    num2 = 3,
    name = 'Chamahk';

// Эта функция объявленна в глобальном scope
function multiply() {
  return num1 * num2;
}

multiply(); // вернёт 60

// Пример вложенной функции
function getScore() {
  var num1 = 2,
      num2 = 3;

  function add() {
    return name + ' scored ' + (num1 + num2);
  }

  return add();
}

getScore(); // вернёт "Chamahk scored 5"
</pre>

<h2 id="Scope_и_стек_функции"><a id="definition" name="definition"></a>Scope и стек функции</h2>

<p><em><u>(function stack)</u></em></p>

<h3 id="Рекурсия">Рекурсия</h3>

<p>Функция может вызывать саму себя. Три способа такого вызова:</p>

<ol>
 <li>по имени функции</li>
 <li><code><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Functions/arguments/callee">arguments.callee</a></code></li>
 <li>по переменной, которая ссылается на функцию</li>
</ol>

<p>Для примера рассмотрим следующие функцию:</p>

<pre class="brush: js">var foo = function bar() {
   // statements go here
};</pre>

<p>Внутри функции (<em>function body</em>) все следующие вызовы эквивалентны:</p>

<ol>
 <li><code>bar()</code></li>
 <li><code>arguments.callee()</code></li>
 <li><code>foo()</code></li>
</ol>

<p>Функция, которая вызывает саму себя, называется <em>рекурсивной функцией</em> (<em>recursive function</em>). Получается, что рекурсия аналогична циклу (<em>loop</em>). Оба вызывают некоторый код несколько раз, и оба требуют условия (чтобы избежать бесконечного цикла, вернее бесконечной рекурсии). Например, следующий цикл:</p>

<pre class="brush: js">var x = 0;
while (x &lt; 10) { // "x &lt; 10" — это условие для цикла
   // do stuff
   x++;
}</pre>

<p>можно было изменить на рекурсивную функцию и вызовом этой функции:</p>

<pre class="brush: js">function loop(x) {
  if (x &gt;= 10) // "x &gt;= 10" — это условие для конца выполнения (тоже самое, что "!(x &lt; 10)")
    return;
  // делать что-то
  loop(x + 1); // рекурсионный вызов
}
loop(0);
</pre>

<p>Однако некоторые алгоритмы не могут быть простыми повторяющимися циклами. Например, получение всех элементов структуры дерева (например, <a href="https://developer.mozilla.org/ru/docs/DOM">DOM</a>) проще всего реализуется использованием рекурсии:</p>

<pre class="brush: js">function walkTree(node) {
  if (node == null) //
    return;
  // что-то делаем с элементами
  for (var i = 0; i &lt; node.childNodes.length; i++) {
    walkTree(node.childNodes[i]);
  }
}</pre>

<p>В сравнении с функцией <code>loop</code>, каждый рекурсивный вызов сам вызывает много рекурсивных вызовов.</p>

<p>Также возможно превращение некоторых рекурсивных алгоритмов в нерекурсивные, но часто их логика очень сложна, и для этого потребуется использование стека (<em>stack</em>). По факту рекурсия использует stack: function stack.</p>

<p>Поведение стека можно увидеть в следующем примере:</p>

<pre class="brush: js">function foo(i) {
  if (i &lt; 0)
    return;
  console.log('begin: ' + i);
  foo(i - 1);
  console.log('end: ' + i);
}
foo(3);

// Output:

// begin: 3
// begin: 2
// begin: 1
// begin: 0
// end: 0
// end: 1
// end: 2
// end: 3</pre>

<h3 id="Вложенные_функции_nested_functions_и_замыкания_closures">Вложенные функции (nested functions) и замыкания (closures)</h3>

<p>Вы можете вложить одну функцию в другую. Вложенная функция (<em>nested function</em>;<em> inner</em>) приватная (<em>private</em>) и она помещена в другую функцию (<em>outer</em>). Так образуется <em>замыкание</em> (<em>closure</em>). Closure — это выражение (обычно функция), которое может иметь свободные переменные вместе со средой, которая связывает эти переменные (что "закрывает" (<em>"close"</em>) выражение).</p>

<p>Поскольку вложенная функция это closure, это означает, что вложенная функция может "унаследовать" (<em>inherit</em>) аргументы и переменные функции, в которую та вложена. Другими словами, вложенная функция содержит scope внешней (<em>"outer"</em>) функции.</p>

<p>Подведём итог:</p>

<ul>
 <li>Вложенная функция имеет доступ ко всем инструкциям внешней функции.</li>
</ul>

<ul>
 <li>Вложенная функция формирует closure: она может использовать аргументы и переменные внешней функции, в то время как внешняя функция не может использовать аргументы и переменные вложенной функции.</li>
</ul>

<p>Следующий пример показывает вложенную функцию:</p>

<pre class="brush: js">function addSquares(a, b) {
  function square(x) {
    return x * x;
  }
  return square(a) + square(b);
}
a = addSquares(2, 3); // возвращает 13
b = addSquares(3, 4); // возвращает 25
c = addSquares(4, 5); // возвращает 41
</pre>

<p>Поскольку вложенная функция формирует closure, вы можете вызвать внешнюю функцию и указать аргументы для обоих функций (для outer и innner).</p>

<pre class="brush: js">function outside(x) {
  function inside(y) {
    return x + y;
  }
  return inside;
}
fn_inside = outside(3); // Подумайте над этим: дайте мне функцию,
                        // который передай 3

result = fn_inside(5); // возвращает 8

result1 = outside(3)(5); // возвращает 8
</pre>

<h3 id="Сохранение_переменных">Сохранение переменных</h3>

<p>Обратите внимание, значение <code>x</code> сохранилось, когда возвращалось <code>inside</code>. Closure должно сохранять аргументы и переменные во всем scope. Поскольку каждый вызов предоставляет потенциально разные аргументы, создаётся новый closure для каждого вызова во вне. Память может быть очищена только тогда, когда <code>inside</code> уже возвратился и больше не доступен.</p>

<p>Это не отличается от хранения ссылок в других объектах, но часто менее очевидно, потому что не устанавливаются ссылки напрямую и нельзя посмотреть там.</p>

<h3 id="Несколько_уровней_вложенности_функций_Multiply-nested_functions">Несколько уровней вложенности функций (Multiply-nested functions)</h3>

<p>Функции можно вкладывать несколько раз, т.е. функция (A) хранит в себе функцию (B), которая хранит в себе функцию (C). Обе функции B и C формируют closures, так B имеет доступ к переменным и аргументам A, и C имеет такой же доступ к B. В добавок, поскольку C имеет такой доступ к B, который имеет такой же доступ к A, C ещё имеет такой же доступ к A. Таким образом closures может хранить в себе несколько scope; они рекурсивно хранят scope функций, содержащих его. Это называется <em>chaining</em> (<em>chain — цепь</em>; Почему названо "chaining" будет объяснено позже)</p>

<p>Рассмотрим следующий пример:</p>

<pre class="brush: js">function A(x) {
  function B(y) {
    function C(z) {
      console.log(x + y + z);
    }
    C(3);
  }
  B(2);
}
A(1); // в консоле выведится 6 (1 + 2 + 3)</pre>

<p>В этом примере C имеет доступ к <code>y</code> функции <code>B</code> и к <code>x</code> функции <code>A</code>. Так получается, потому что:</p>

<ol>
 <li>Функция <code>B</code> формирует closure, включающее <code>A</code>, т.е. <code>B</code> имеет доступ к аргументам и переменным функции <code>A</code>.</li>
 <li>Функция <code>C</code> формирует closure, включающее <code>B</code>.</li>
 <li>Раз closure функции <code>B</code> включает <code>A</code>, то closure <code>С</code> тоже включает A, <code>C</code> имеет доступ к аргументам и переменным обоих функций <code>B</code> <em>и</em> <code>A</code>. Другими словами, <code>С</code> связывает <em>цепью</em> (<em>chain</em>) scopes функций <code>B</code> и <code>A</code> в таком порядке.</li>
</ol>

<p>В обратном порядке, однако, это не верно. <code>A</code> не имеет доступ к переменным и аргументам <code>C</code>, потому что <code>A</code> не имеет такой доступ к <code>B</code>. Таким образом, <code>C</code> остаётся приватным только для <code>B</code>.</p>

<h3 id="Конфликты_имён_Name_conflicts">Конфликты имён (Name conflicts)</h3>

<p>Когда два аргумента или переменных в scope у closure имеют одинаковые имена, происходит <em>конфликт имени</em> (<em>name conflict</em>). Более вложенный (<em>more inner</em>) scope имеет приоритет, так самый вложенный scope имеет наивысший приоритет, и наоборот. Это цепочка областей видимости (<em>scope chain</em>). Самым первым звеном является самый глубокий scope, и наоборот. Рассмотрим следующие:</p>

<pre class="brush: js">function outside() {
  var x = 5;
  function inside(x) {
    return x * 2;
  }
  return inside;
}

outside()(10); // возвращает 20 вместо 10</pre>

<p>Конфликт имени произошёл в инструкции <code>return x * 2</code> между параметром <code>x</code> функции <code>inside</code> и переменной <code>x</code> функции <code>outside</code>. Scope chain здесь будет таким: {<code>inside</code> ==&gt; <code>outside</code> ==&gt; глобальный объект (<em>global object</em>)}. Следовательно <code>x</code> функции <code>inside</code> имеет больший приоритет по сравнению с <code>outside</code>, и нам вернулось 20 (= 10 * 2), а не 10 (= 5 * 2).</p>

<h2 id="Замыкания"><a id="definition" name="definition"></a>Замыкания</h2>

<p><em><u>(Closures)</u></em></p>

<p>Closures это один из главных особенностей JavaScript. JavaScript разрешает вложенность функций и предоставляет вложенной функции полный доступ ко всем переменным и функциям, объявленным внутри внешней функции (и другим переменным и функции, к которым имеет доступ эта внешняя функция).</p>

<p>Однако, внешняя функция не имеет доступа к переменным и функциям, объявленным во внутренней функции. Это обеспечивает своего рода инкапсуляцию для переменных внутри вложенной функции.</p>

<p>Также, поскольку вложенная функция имеет доступ к scope внешней функции, переменные и функции, объявленные во внешней функции, будет продолжать существовать и после её выполнения для вложенной функции, если на них и на неё сохранился доступ (имеется ввиду, что переменные, объявленные во внешней функции, сохраняются, только если внутренняя функция обращается к ним).</p>

<p>Closure создаётся, когда вложенная функция как-то стала доступной в неком scope вне внешней функции.</p>

<pre class="brush: js">var pet = function(name) {   // Внешняя функция объявила переменную "name"
  var getName = function() {
    return name;             // Вложенная функция имеет доступ к "name" внешней функции
  }
  return getName;            // Возвращаем вложенную функцию, тем самым сохраняя доступ
                             // к ней для другого scope
}
myPet = pet('Vivie');

myPet();                     // Возвращается "Vivie",
                             // т.к. даже после выполнения внешней функции
                             // name сохранился для вложенной функции
</pre>

<p>Более сложный пример представлен ниже. Объект с методами для манипуляции вложенной функции внешней функцией можно вернуть (<em>return</em>).</p>

<pre class="brush: js">var createPet = function(name) {
  var sex;

  return {
    setName: function(newName) {
      name = newName;
    },

    getName: function() {
      return name;
    },

    getSex: function() {
      return sex;
    },

    setSex: function(newSex) {
      if(typeof newSex === 'string' &amp;&amp; (newSex.toLowerCase() === 'male' ||
        newSex.toLowerCase() === 'female')) {
        sex = newSex;
      }
    }
  }
}

var pet = createPet('Vivie');
pet.getName();                  // Vivie

pet.setName('Oliver');
pet.setSex('male');
pet.getSex();                   // male
pet.getName();                  // Oliver</pre>

<p>В коде выше переменная <code>name</code> внешней функции доступна для вложенной функции, и нет другого способа доступа к вложенным переменным кроме как через вложенную функцию. Вложенные переменные вложенной функции являются безопасными хранилищами для внешних аргументов и переменных. Они содержат "постоянные" и "инкапсулированные" данные для работы с ними вложенными функциями. Функции даже не должны присваиваться переменной или иметь имя.</p>

<pre class="brush: js">var getCode = (function() {
  var apiCode = '0]Eal(eh&amp;2'; // A code we do not want outsiders to be able to modify...

  return function() {
    return apiCode;
  };
}());

getCode();    // Returns the apiCode
</pre>

<p>Однако есть ряд подводных камней, которые следует учитывать при использовании замыканий. Если закрытая функция определяет переменную с тем же именем, что и имя переменной во внешней области, нет способа снова ссылаться на переменную во внешней области.</p>

<pre class="brush: js">var createPet = function(name) {  // The outer function defines a variable called "name".
  return {
    setName: function(name) {    // The enclosed function also defines a variable called "name".
      name = name;               // How do we access the "name" defined by the outer function?
    }
  }
}
</pre>

<h2 id="Использование_объекта_arguments"><a id="definition" name="definition"></a>Использование объекта arguments</h2>

<p>Объект arguments функции является псевдо-массивом. Внутри функции вы можете ссылаться к аргументам следующим образом:</p>

<pre class="brush: js">arguments[i]
</pre>

<p>где <code>i</code> — это порядковый номер аргумента, отсчитывающийся с 0. К первому аргументу, переданному функции, обращаются так <code>arguments[0]</code>. А получить количество всех аргументов — <code>arguments.length</code>.</p>

<p>С помощью объекта <code>arguments</code> Вы можете вызвать функцию, передавая в неё больше аргументов, чем формально объявили принять. Это очень полезно, если вы не знаете точно, сколько аргументов должна принять ваша функция. Вы можете использовать <code>arguments.length</code> для определения количества аргументов, переданных функции, а затем получить доступ к каждому аргументу, используя объект <code>arguments</code>.</p>

<p>Для примера рассмотрим функцию, которая конкатенирует несколько строк. Единственным формальным аргументом для функции будет строка, которая указывает символы, которые разделяют элементы для конкатенации. Функция определяется следующим образом:</p>

<pre class="brush: js">function myConcat(separator) {
   var result = '';
   var i;

   // iterate through arguments
   for (i = 1; i &lt; arguments.length; i++) {
      result += arguments[i] + separator;
   }
   return result;
}</pre>

<p>Вы можете передавать любое количество аргументов в эту функцию, и он конкатенирует каждый аргумент в одну строку.</p>

<pre class="brush: js">// возвращает "red, orange, blue, "
myConcat(', ', 'red', 'orange', 'blue');

// возвращает "elephant; giraffe; lion; cheetah; "
myConcat('; ', 'elephant', 'giraffe', 'lion', 'cheetah');

// возвращает "sage. basil. oregano. pepper. parsley. "
myConcat('. ', 'sage', 'basil', 'oregano', 'pepper', 'parsley');
</pre>

<div class="note">
<p><strong>Примечание:</strong> <code>arguments</code> является псевдо-массивом, но не массивом. Это псевдо-массив, в котором есть пронумерованные индексы и свойство <code>length</code>. Однако он не обладает всеми методами массивов.</p>
</div>

<p>Рассмотрите объект <code><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Function" title="The Function constructor creates a new Function object. Calling the constructor directly can create functions dynamically, but suffers from security and similar (but far less significant) performance issues similar to eval. However, unlike eval, the Function constructor allows executing code in the global scope, prompting better programming habits and allowing for more efficient code minification.">Function</a></code> в JavaScript-справочнике для большей информации.</p>

<h2 id="Параметры_функции"><a id="definition" name="definition"></a>Параметры функции</h2>

<p>Начиная с ECMAScript 2015 появились два новых вида параметров: параметры по умолчанию (<em>default parameters</em>) и остаточные параметры (<em>rest parameters</em>).</p>

<h3 id="Параметры_по_умолчанию_Default_parameters">Параметры по умолчанию (Default parameters)</h3>

<p>В JavaScript параметры функции по умолчанию имеют значение <code>undefined</code>. Однако в некоторых ситуация может быть полезным поменять значение по умолчанию. В таких случаях default parameters могут быть весьма кстати.</p>

<p>В прошлом для этого было необходимо в теле функции проверять значения параметров на <code>undefined</code> и в положительном случае менять это значение на дефолтное (<em>default</em>). В следующем примере в случае, если при вызове не предоставили значение для <code>b</code>, то этим значением станет <code>undefined</code>, тогда результатом вычисления <code>a * b</code> в функции <code>multiply</code> будет <code>NaN</code>. Однако во второй строке мы поймаем это значение:</p>

<pre class="brush: js">function multiply(a, b) {
  b = typeof b !== 'undefined' ?  b : 1;

  return a * b;
}

multiply(5); // 5</pre>

<p>С параметрами по умолчанию проверка наличия значения параметра в теле функции не нужна. Теперь вы можете просто указать значение по умолчанию для параметра <code>b</code> в объявлении функции:</p>

<pre class="brush: js">function multiply(a, b = 1) {
  return a * b;
}

multiply(5); // 5</pre>

<p>Для более детального рассмотрения ознакомьтесь с <a href="/ru/docs/Web/JavaScript/Reference/Functions/Default_parameters">параметрами по умолчанию</a>.</p>

<h3 id="Остаточные_параметры_Rest_parameters">Остаточные параметры (Rest parameters)</h3>

<p><a href="/ru/docs/Web/JavaScript/Reference/Functions/Rest_parameters">Остаточные параметры</a> предоставляют нам массив неопределённых аргументов. В примере мы используем остаточные параметры, чтобы собрать аргументы с индексами со 2-го до последнего. Затем мы умножим каждый из них на значение первого аргумента. В этом примере используется стрелочная функция (<u><em><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Functions/Arrow_functions">Arrow functions</a>)</em></u>, о которой будет рассказано в следующей секции.</p>

<pre class="brush: js">function multiply(multiplier, ...theArgs) {
  return theArgs.map(x =&gt; multiplier * x);
}

var arr = multiply(2, 1, 2, 3);
console.log(arr); // [2, 4, 6]
</pre>

<h2 id="Стрелочные_функции"><a id="definition" name="definition"></a>Стрелочные функции</h2>

<p><u><em>(Arrow functions)</em></u></p>

<p><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Functions/Arrow_functions">Стрелочные функции</a> — функции вида "arrow function expression" (неверно <strong>fat arrow function</strong>) — имеют укороченный синтаксис по сравнению с function expression и лексически связывает значение <code>this</code>. Стрелочные функции всегда анонимны. Посмотрите также пост блога hacks.mozilla.org "<a href="https://hacks.mozilla.org/2015/06/es6-in-depth-arrow-functions/" rel="noopener">ES6 In Depth: Arrow functions</a>".</p>

<p>На введение стрелочных функций повлияли два фактора: более короткие функции и лексика <code>this</code>.</p>

<h3 id="Более_короткие_функции">Более короткие функции</h3>

<p>В некоторых функциональных паттернах приветствуется использование более коротких функций. Сравните:</p>

<pre class="brush: js">var a = [
  'Hydrogen',
  'Helium',
  'Lithium',
  'Beryllium'
];

var a2 = a.map(function(s) { return s.length; });

console.log(a2); // logs [8, 6, 7, 9]

var a3 = a.map(s =&gt; s.length);

console.log(a3); // logs [8, 6, 7, 9]</pre>

<h3 id="Лексика_this">Лексика <code>this</code></h3>

<p>До стрелочных функций каждая новая функция определяла своё значение <code>this</code> (новый объект в случае конструктора, undefined в strict mode,  контекстный объект, если функция вызвана как метод объекта, и т.д.).  Это оказалось раздражающим с точки зрения объектно-ориентированного стиля программирования.</p>

<pre class="brush: js">function Person() {
  // Конструктор Person() определяет `this` как самого себя.
  this.age = 0;

  setInterval(function growUp() {
    // Без strict mode функция growUp() определяет `this`
    // как global object, который отличается от `this`
    // определённого конструктором Person().
    this.age++;
  }, 1000);
}

var p = new Person();</pre>

<p>В ECMAScript 3/5 эта проблема была исправлена путём присвоения значения <code>this</code> переменной, которую можно было бы замкнуть.</p>

<pre class="brush: js">function Person() {
  var self = this; // Некоторые выбирают `that` вместо `self`.
                   // Выберите что-то одно и будьте последовательны.
  self.age = 0;

  setInterval(function growUp() {
    // The callback refers to the `self` variable of which
    // the value is the expected object.
    self.age++;
  }, 1000);
}</pre>

<p>Альтернативой может быть <a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Function/bind">связанная функция</a> (<em>bound function</em>), с которой можно правильно вручную определить значение <code>this</code> для функции <code>growUp()</code>.</p>

<p>В arrow function значением <code>this</code> является окружающий его контекст, так следующий код работает ожидаемо:</p>

<pre class="brush: js">function Person() {
  this.age = 0;

  setInterval(() =&gt; {
    this.age++; // |this| должным образом ссылается на объект Person
  }, 1000);
}

var p = new Person();

</pre>

<h2 id="Далее">Далее</h2>

<p>Подробное техническое описание функций в статье справочника {{jsxref("Functions","Функции")}}</p>

<p>Смотрите также <a href="/en-US/docs/JavaScript/Reference/Global_Objects/Function" title="en-US/docs/JavaScript/Reference/Global Objects/Function"><code>Function</code></a> в Справочнике JavaScript для получения дополнительной информации по функции как объекту.</p>

<p>Внешние ресурсы:</p>

<ul>
 <li><a href="http://www.ecma-international.org/ecma-262/6.0/index.html#sec-ecmascript-function-objects">ECMAScript® 2015 Language Specification</a></li>
 <li><a href="https://learn.javascript.ru/closures">Учебник по Javascript - замыкания</a></li>
</ul>

<p>{{PreviousNext("Web/JavaScript/Guide/Loops_and_iteration", "Web/JavaScript/Guide/Expressions_and_Operators")}}</p>