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
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
|
---
title: Повторное введение в JavaScript (JS учебник)
slug: Web/JavaScript/A_re-introduction_to_JavaScript
translation_of: Web/JavaScript/A_re-introduction_to_JavaScript
---
<div>{{jsSidebar}}</div>
<p>Почему повторное введение? Потому что {{Glossary("JavaScript")}} известен тем, что является <a href="http://javascript.crockford.com/javascript.html">самым неправильно понятым языком программирования в мире</a>. Его часто называют игрушкой, но под слоем обманчивой простоты ожидают мощные языковые возможности. В настоящее время JavaScript используется невероятным количеством высококлассных приложений, показывая, что углублённое знание этой технологии является важным навыком для любого веб или мобильного разработчика.</p>
<p>Было бы полезно начать с истории языка. JavaScript был создан в 1995 Бренданом Айком, инженером в компании Netscape. Первый релиз состоялся вместе с выходом браузера Netscape 2 в начале 1996 года. Сначала язык назывался LiveScript, но затем был переименован в связи с маркетинговыми целями, чтобы сыграть на популярности языка Java компании Sun Microsystem — несмотря на это языки практически не имеют ничего общего друг с другом. Так было положено начало путаницы между этими языками.</p>
<p>Чуть позже Microsoft выпустила очень похожий и практически совместимый язык JScript, который шёл вместе с IE3. Через пару месяцев Netscape отправил язык в <a href="http://www.ecma-international.org/">Ecma International</a>, Европейскую организацию занимающуюся стандартами, которая выпустила первую версию стандарта <a href="https://developer.mozilla.org/en/JavaScript/Language_Resources" title="en/ECMAScript">ECMAScript</a> в 1997. Стандарт получил значимое обновление в <a href="http://www.ecma-international.org/publications/standards/Ecma-262.htm">ECMAScript edition 3</a> в 1999, и остаётся самым стабильным до сегодняшнего дня. Четвёртая версия была отклонена, из-за проблем с усложнениями в языке. Многие вещи из четвёртого издания послужили основой для стандарта ECMAScript 5 (декабрь 2009) и ECMAScript 6 (июнь 2015).</p>
<div class="note">
<p><strong>На заметку</strong>: Далее по тексту мы будем называть язык ECMAScript как "JavaScript".</p>
</div>
<p>В отличие от большинства языков, JavaScript не следует концепции ввода (input) и вывода (output). Он спроектирован таким образом, чтобы запускаться как язык сценариев, встроенный в среду исполнения. Самая популярная среда исполнения это браузер, однако интерпретаторы JavaScript присутствуют и в Adobe Acrobat, Photoshop, Yahoo!'s Widget engine, и даже в серверном окружении, например <a href="http://nodejs.org/" title="nodejs.org">node.js</a>.</p>
<h2 id="Описание">Описание</h2>
<p>JavaScript является объектно-ориентированным языком, имеющий типы и операторы, встроенные объекты и методы. Его синтаксис происходит от языков Java и C, поэтому много конструкций из этих языков применимы и к JavaScript. Одним из ключевых отличий JavaScript является отсутствие классов, вместо этого функциональность классов осуществляется прототипами объектов (смотрите ES6 {{jsxref("Classes")}}) . Другое главное отличие в том, что функции это объекты, в которых содержится исполняемый код и которые могут быть переданы куда-либо, как и любой другой объект.</p>
<p>Начнём с основы любого языка: с типов данных. Программы на JavaScript оперируют значениями, и все эти значения принадлежат к определённому типу. Типы данных в JavaScript:</p>
<ul>
<li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Number" title="en/Core_JavaScript_1.5_Reference/Global_Objects/Number">Числа</a></li>
<li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/String" title="en/Core_JavaScript_1.5_Reference/Global_Objects/String">Строки</a></li>
<li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Boolean" title="en/Core_JavaScript_1.5_Reference/Global_Objects/Boolean">Логические типы</a></li>
<li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Function" title="en/Core_JavaScript_1.5_Reference/Global_Objects/Function">Функции</a></li>
<li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Object" title="en/Core_JavaScript_1.5_Reference/Global_Objects/Object">Объекты</a></li>
<li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Symbol">Символы</a> (новый тип из шестой редакции)</li>
</ul>
<p>Да, ещё <a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/undefined">Undefined</a> и <a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/null">Null</a>, которые немного обособлены. И <a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Array" title="en/Core_JavaScript_1.5_Reference/Global_Objects/Array">Массивы</a>, которые являются особым видом объектов. А также <a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Date" title="en/Core_JavaScript_1.5_Reference/Global_Objects/Date">Даты</a> и <a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/RegExp" title="en/Core_JavaScript_1.5_Reference/Global_Objects/RegExp">Регулярные выражения</a>, тоже являющиеся объектами. И, если быть технически точным, функции это тоже особый вид объекта. Поэтому схема типов выглядит скорее так:</p>
<ul>
<li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Number">Числа</a></li>
<li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/String">Строки</a></li>
<li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Boolean">Логические типы</a></li>
<li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Symbol">Символы</a> (новый тип из шестой редакции)</li>
<li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Object">Объекты</a>
<ul>
<li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Function">Функции</a></li>
<li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Array">Массивы</a></li>
<li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Date">Даты</a></li>
<li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/RegExp">Регулярные выражения</a></li>
</ul>
</li>
<li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/null">Null</a></li>
<li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/undefined">undefined</a></li>
</ul>
<p>Также есть несколько встроенных типов <a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Error" title="en/Core_JavaScript_1.5_Reference/Global_Objects/Error">Ошибок</a>. Чтобы было проще, рассмотрим подробнее первую схему.</p>
<h2 id="Числа">Числа</h2>
<p>Числа в JavaScript — это "64-битные значения двойной точности формата IEEE 754", согласно спецификации. Это имеет интересные последствия. В JavaScript нет такой вещи, как целое число, поэтому с арифметикой нужно быть начеку, если вы привыкли к вычислениям в языках C или Java. Взгляните на пример:</p>
<pre class="brush: js notranslate">0.1 + 0.2 == 0.30000000000000004
</pre>
<p>На практике целые значения это 32-битные целые (и хранятся таким образом в некоторых браузерных реализациях), что может быть важно для побитовых операций.</p>
<p>Поддерживаются стандартные <a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators" title="en/Core_JavaScript_1.5_Reference/Operators/Arithmetic_Operators">арифметические операторы</a>, включая сложение, вычитание, остаток от деления и т.д. Есть ещё встроенный объект, который я забыл упомянуть, называемый <a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Math" title="en/Core_JavaScript_1.5_Reference/Global_Objects/Math">Math</a>, который содержит более продвинутые математические функции и константы:</p>
<pre class="brush: js notranslate">Math.sin(3.5);
var circumference = Math.PI * (r + r);
</pre>
<p>Вы можете преобразовать строку в целое число, используя встроенную функцию <code><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/parseInt" title="en/Core_JavaScript_1.5_Reference/Global_Functions/parseInt">parseInt()</a></code>. Её необязательный второй параметр — основание системы счисления, которое следует всегда явно указывать:</p>
<pre class="brush: js notranslate">parseInt("123", 10); // 123
parseInt("010", 10); // 10
</pre>
<p>Если вы не предоставите основание, то можете получить неожиданные результаты:</p>
<pre class="brush: js notranslate">parseInt("010"); // 8
parseInt("0x10"); // 16
</pre>
<p>Это случилось потому, что функция <code><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/parseInt">parseInt()</a></code> расценила строку как восьмеричную из-за начального 0, а шестнадцатеричную - из-за начального "0x".</p>
<p>Если хотите преобразовать двоичное число в десятичное целое, просто смените основание:</p>
<pre class="brush: js notranslate">parseInt("11", 2); // 3
</pre>
<p>Вы можете аналогично парсить дробные числа, используя встроенную функцию <code><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/parseFloat" title="en/JavaScript/Reference/Global Objects/parseFloat">parseFloat()</a></code>, которая использует всегда основание 10 в отличие от родственной <a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/parseInt" title="en/JavaScript/Reference/Global Objects/parseInt"><code>parseInt()</code></a>.</p>
<p>Также можно использовать унарный оператор <strong><code>+</code></strong> для преобразования значения в число:</p>
<pre class="brush: js notranslate">+ "42"; // 42
+ "0x10"; // 16
</pre>
<p>Специальное значение <a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/NaN">NaN</a> (сокращение от "Not a Number") возвращается, если строка не является числом:</p>
<pre class="brush: js notranslate">parseInt("hello", 10); // NaN
</pre>
<p><code>NaN</code> "заразителен": любая математическая операция над <code>NaN</code> возвращает <code>NaN</code>:</p>
<pre class="brush: js notranslate">NaN + 5; // NaN
</pre>
<p>Проверить значение на <code>NaN</code> можно встроенной функцией <code><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/isNaN" title="en/Core_JavaScript_1.5_Reference/Global_Functions/isNaN">isNaN()</a></code>:</p>
<pre class="brush: js notranslate">isNaN(NaN); // true
</pre>
<p>JavaScript также имеет специальные значения <code><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Infinity" title="en/Core_JavaScript_1.5_Reference/Global_Properties/Infinity">Infinity</a></code> (бесконечность) и<code> -Infinity</code>:</p>
<pre class="brush: js notranslate">1 / 0; // Infinity
-1 / 0; // -Infinity
</pre>
<p>Проверить значение на <code>Infinity</code>, <code>-Infinity</code> и <code>NaN</code> можно с помощью встроенной функции <code><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/isFinite" title="en/Core_JavaScript_1.5_Reference/Global_Functions/isFinite">isFinite()</a></code>:</p>
<pre class="brush: js notranslate">isFinite(1/0); // false
isFinite(-Infinity); // false
isFinite(NaN); // false
</pre>
<div class="note"><strong>Примечание:</strong> функции <a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/parseInt" title="en/JavaScript/Reference/Global Objects/parseInt"><code>parseInt()</code></a> и <code><a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/parseFloat" title="en/JavaScript/Reference/Global Objects/parseFloat">parseFloat()</a></code> обрабатывают строку до тех пор, пока не будет встречен символ, не являющийся корректным для заданного числового формата, затем эти функции возвращают число, полученное в результате обработки вплоть до этого символа. А оператор "+" просто возвращает <code>NaN</code>, если в строке есть хоть один некорректный символ. Попробуйте сами в консоли преобразовать строку "10.2abc" каждым из методов, и различие станет ясным.</div>
<h2 id="Строки">Строки</h2>
<p>Строки в JavaScript - это последовательности <a href="/ru/docs/Web/JavaScript/Guide/Grammar_and_types" title="en/Core_JavaScript_1.5_Guide/Unicode">символов Unicode</a> (в кодировке UTF-16). Для тех, кто имеет дело с интернационализацией, это должно стать хорошей новостью. Если быть более точным, то строка - это последовательность кодовых единиц, каждая из которых представлена 16-битовым числом, а каждый символ Unicode состоит из 1 или 2 кодовых единиц.</p>
<p>Чтобы представить единственный символ, используйте строку, содержащую только этот символ.</p>
<p>Чтобы выяснить длину строки (в кодовых единицах), используйте свойство <code><a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/String/length" title="en/Core_JavaScript_1.5_Reference/Global_Objects/String/length">length</a></code>:</p>
<pre class="brush: js notranslate">"hello".length; // 5
</pre>
<p>Это уже первый шаг для работы с объектами! Мы уже говорили, что и строки можно использовать как объекты? У них тоже есть <a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/String" title="en/Core_JavaScript_1.5_Reference/Global_Objects/String#Methods">методы</a>:</p>
<pre class="brush: js notranslate">"hello".charAt(0); // h
"hello, world".replace("hello", "goodbye"); // goodbye, world
"hello".toUpperCase(); // HELLO
</pre>
<h2 id="Другие_типы">Другие типы</h2>
<p>JavaScript дополнительно различает такие типы, как <code>null</code>, который указывает на преднамеренное отсутствующее значение, и <code>undefined</code>, указывающий на неинициализированное значение — то есть, значение, которое даже не было назначено. Мы поговорим о переменных позже, но в JavaScript можно объявить переменную без присвоения ей значения. В этом случае тип переменной будет <code>"undefined"</code>.</p>
<p>Ещё в JavaScript есть логический (булевый) тип данных, который может принимать два возможных значения <span style="font-family: consolas,monaco,andale mono,monospace;">true</span> или <span style="font-family: consolas,monaco,andale mono,monospace;">false</span> (оба являются ключевыми словами). Любое значение может быть преобразовано в логическое значение в соответствии со следующими правилами:</p>
<ol>
<li><code>false</code>, <code>0</code>, пустая строка (<code>""</code>), <code>NaN</code>, <code>null</code> и <code>undefined</code> преобразуются в <code>false.</code></li>
<li>Все остальные значения преобразуются в <code>true.</code></li>
</ol>
<p>Преобразование значений можно осуществить явно, используя функцию <code>Boolean()</code>:</p>
<pre class="brush: js notranslate">Boolean(""); // false
Boolean(234); // true
</pre>
<p>Этот метод используется редко, так как JavaScript может автоматически преобразовывать типы в тех случаях, когда ожидается булевое значение, например в операторе <code>if</code>. Из-за того, что любой тип данных может быть преобразован в булевое значение, иногда говорят, что данные "истинные" или "ложные".</p>
<p>Для операций с логическими данными используются логические операторы: <code>&&</code> (логическое <em>И</em>), <code>||</code> (логическое <em>ИЛИ</em>), <code>!</code> (логическое <em>НЕ</em>).</p>
<h2 id="Переменные">Переменные</h2>
<p>Для объявления новых переменных в JavaScript используются ключевые слова <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let">let</a></code>, <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const">const</a></code> или <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var" title="/en/JavaScript/Reference/Statements/var">var</a></code>.</p>
<pre class="brush: js notranslate">let a;
let name = "Simon";
</pre>
<p><strong><code>let</code> </strong>позволяет объявлять переменные, которые доступны только в <em><strong>блоке</strong>,</em> в котором они объявлены:</p>
<pre class="brush: js notranslate">// myLetVariable недоступна здесь
for (let myLetVariable = 0; myLetVariable < 5; myLetVariable++) {
// myLetVariable доступна только здесь
}
// myLetVariable недоступна здесь</pre>
<p><code><strong>const</strong></code> позволяет создавать переменные, чьи значения не предполагают изменений. Переменная доступна из <em><strong>блока</strong></em>, в котором она объявлена.</p>
<pre class="brush: js notranslate">const Pi = 3.14; // в переменную Pi записано значение.
Pi = 1; // вызовет исключение, так как значение константы нельзя изменить.</pre>
<p><code><strong>var</strong></code> наиболее общее средство объявления переменной. Оно не имеет ограничений, которые имеют два вышеописанных способа. Это потому, что это был изначально единственный способ объявления переменной в JavaScript. Переменная, объявленная с помощью <strong><code>var</code>, </strong>доступна в пределах <strong><em>функции</em></strong>, в которой она объявлена.</p>
<pre class="brush: js notranslate">var a;
var name = 'Simon';</pre>
<p>Пример кода с переменной, объявленной с помощью <strong><code>var</code>:</strong></p>
<pre class="brush: js notranslate">// myVarVariable доступна здесь
for (var myVarVariable = 0; myVarVariable < 5; myVarVariable++) {
// myVarVariable доступна для всей функции
}
// myVarVariable доступна и здесь</pre>
<p>Если вы объявляете переменную без присвоения ей какого-либо значения, то её тип будет определён как <code>undefined</code>. </p>
<p>Важной особенностью языка JavaScript является то, что блоки данных не имеют своей области видимости, она есть только у функций. Поэтому, если объявить переменную через <code>var</code> в блоке данных (например, внутри контролирующей структуры <code>if</code>), то она будет доступна всей функции. Следует отметить, что в новом стандарте ECMAScript Edition 6 появились инструкции <code><a href="/ru/docs/Web/JavaScript/Reference/Statements/let">let</a></code> и <code><a href="/ru/docs/Web/JavaScript/Reference/Statements/const">const</a></code>, позволяющие объявлять переменные с областью видимости, ограниченной пределами блока.</p>
<h2 id="Операторы">Операторы</h2>
<p>JavaScript поддерживает такие операторы, как <code>+</code>, <code>-</code>, <code>*</code>, <code>/</code> и <code>%</code>, который возвращает остаток от деления (<a href="/ru/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Remainder_%28%29">не путать с модулем</a>). Значения присваиваются с помощью оператора <code>=</code>, или с помощью составных операторов <code>+=</code> и <code>-=</code>. Это сокращённая запись выражения <code><strong>x = x </strong><em>оператор</em><strong> y</strong></code>.</p>
<pre class="brush: js notranslate">x += 5
x = x + 5
</pre>
<p>Так же используются операторы инкремента (<code>++</code>) и декремента (<code>--</code>). Которые имеют префиксную и постфиксную форму записи.</p>
<p><a href="/ru/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators" title="en/Core_JavaScript_1.5_Reference/Operators/String_Operators">Оператор <code>+</code></a> так же выполняет конкатенацию (объединение) строк:</p>
<pre class="brush: js notranslate">"hello" + " world"; // "hello world"
</pre>
<p>При сложении строкового и числового значений происходит автоматическое преобразование в строку. Поначалу такое может запутать:</p>
<pre class="brush: js notranslate">"3" + 4 + 5; // "345"
3 + 4 + "5"; // "75"
</pre>
<p>Для приведения значения к строке просто прибавьте к нему пустую строку.</p>
<p>Для <a href="/ru/docs/Web/JavaScript/Reference/Operators/Comparison_Operators" title="en/Core_JavaScript_1.5_Reference/Operators/Comparison_Operators">сравнения</a> в JavaScript используются следующие операторы: <code><</code>, <code>></code>, <code><=</code> и <code>>=</code>. Сравнивать можно не только числа, но и строки. Проверка на равенство немного сложнее. Для проверки используют двойной (<code>==</code>) или тройной (<code>===</code>) оператор присваивания. Двойной оператор <code>==</code> осуществляет автоматическое преобразование типов, что может приводить к интересным результатам:</p>
<pre class="brush: js notranslate">123 == "123"; // true
1 == true; // true</pre>
<p>Если преобразование нежелательно, то используют оператор строгого равенства:</p>
<pre class="brush: js notranslate">1 === true; // false
123 === "123"; // false
true === true; // true</pre>
<p>Для проверки на неравенство используют операторы <code>!=</code> и <code>!==</code>.</p>
<p>Отдельного внимания стоят <a href="/ru/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators">побитовые операторы</a>, с которыми вы можете ознакомиться в соответствующем разделе.</p>
<h2 id="Управляющие_структуры">Управляющие структуры</h2>
<p>Управляющие структуры в JavaScript очень похожи на таковые в языках семейства C. Условные операторы выражены ключевыми словами <code>if</code> и <code>else</code>, которые можно составлять в цепочки:</p>
<pre class="brush: js notranslate">var name = "kittens";
if (name == "puppies") {
name += "!";
} else if (name == "kittens") {
name += "!!";
} else {
name = "!" + name;
}
name == "kittens!!"
</pre>
<p>В JavaScript есть три типа циклов: <code>while</code>, <code>do-while</code> и <code>for</code>. While используется для задания обычного цикла, а do-while целесообразно применить в том случае, если вы хотите, чтобы цикл был выполнен хотя бы один раз:</p>
<pre class="brush: js notranslate">while (true) {
// бесконечный цикл!
}
var input;
do {
input = get_input();
} while (inputIsNotValid(input))
</pre>
<p>Цикл <code>for</code> похож на такой же в языках C и Java: он позволяет задавать данные для контроля за выполнением цикла:</p>
<pre class="brush: js notranslate">for (var i = 0; i < 5; i++) {
// Выполнится 5 раз
}
</pre>
<p>JavaScript также содержит две других известных конструкции: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of"><code>for</code>...<code>of</code></a></p>
<pre class="brush: js notranslate">for (let value of array) {
// операции с value
}</pre>
<p>и <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in"><code>for</code>...<code>in</code></a>:</p>
<pre class="brush: js notranslate">for (let property in object) {
// операции над свойствами объекта
}</pre>
<p>Логические операторы <code>&&</code> и <code>||</code> используют "короткий цикл вычисления", это значит, что вычисление каждого последующего оператора зависит от предыдущего. Например, полезно проверить существует ли объект или нет, прежде чем пытаться получить доступ к его свойствам:</p>
<pre class="brush: js notranslate">var name = o && o.getName();
</pre>
<p>Таким способом удобно задавать значения по умолчанию:</p>
<pre class="brush: js notranslate">var name = otherName || "default";
</pre>
<p>К условным операторам в JavaScript принадлежит также тернарный оператор "<code>?</code>" :</p>
<pre class="brush: js notranslate">var allowed = (age > 18) ? "yes" : "no";
</pre>
<p>Оператор <code>switch</code> используется при необходимости множественного сравнения:</p>
<pre class="brush: js notranslate">switch(action) {
case 'draw':
drawit();
break;
case 'eat':
eatit();
break;
default:
donothing();
}
</pre>
<p>Если в конце инструкции <code>case</code> не добавить останавливающую инструкцию <code>break</code>, то выполнение перейдёт к следующей инструкции <code>case</code>. Как правило, такое поведение нежелательно, но если вдруг вы решили его использовать, настоятельно рекомендуем писать соответствующий комментарий для облегчения поиска ошибок:</p>
<pre class="brush: js notranslate">switch(a) {
case 1: // fallthrough
case 2:
eatit();
break;
default:
donothing();
}
</pre>
<p>Вариант <code>default</code> опциональный. Допускается использование выражений как в условии <code>switch</code>, так и в <code>cases</code>. При проверке на равенство используется оператор строгого равенства <code>===</code>:</p>
<pre class="brush: js notranslate">switch(1 + 3) {
case 2 + 2:
yay();
break;
default:
neverhappens();
}
</pre>
<h2 id="Объекты">Объекты</h2>
<p>Объекты в JavaScript представляют собой коллекции пар имя-значение (ключ-значение). Они похожи на:</p>
<ul>
<li>Словари в Python.</li>
<li>Хеши в Perl и Ruby.</li>
<li>Таблицы хешей в C и C++.</li>
<li>HashMaps в Java.</li>
<li>Ассоциативные массивы в PHP.</li>
</ul>
<p>Именем свойства объекта в JavaScript выступает строка, а значением может быть любой тип данных JavaScript, даже другие объекты. Это позволяет создавать структуры данных любой сложности.</p>
<p>Существует два основных способа создать объект:</p>
<pre class="brush: js notranslate">var obj = new Object();
</pre>
<p>А также:</p>
<pre class="brush: js notranslate">var obj = {};
</pre>
<p>Обе эти записи делают одно и то же. Вторая запись называется литералом объекта и более удобная. Такой способ является основой формата JSON, и при написании кода лучше использовать именно его.</p>
<p>С помощью литерала объекта можно создавать не только пустые объекты, но и объекты с данными:</p>
<pre class="brush: js notranslate">var obj = {
name: "Carrot",
"for": "Max",
details: {
color: "orange",
size: 12
}
}
</pre>
<p>Доступ к свойствам объекта можно получить следующими способами:</p>
<pre class="brush: js notranslate">obj.details.color; // orange
obj['details']['size']; // 12</pre>
<p>Эти два метода равнозначны. Первый метод используется, если мы точно знаем к какому методу нам нужно обратиться. Второй метод принимает в качестве имени свойства строку, и позволяет вычислять имя в процессе вычислений. Следует отметить, что последний метод мешает некоторым движкам и минимизаторам оптимизировать код. Если появится необходимость назначить в качестве имён свойств объекта <a href="/ru/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords" title="en/Core_JavaScript_1.5_Reference/Reserved_Words">зарезервированные слова</a>, то данный метод тоже может пригодиться:</p>
<pre class="brush: js notranslate">// Вызовет Syntax error, ведь 'for' это зарезервированное слово
obj.for = "Simon";
// А тут всё нормально
obj["for"] = "Simon";
</pre>
<div class="note">
<p><strong>На заметку:</strong> Стандарт EcmaScript 5 позволяет использовать зарезервированные слова в качестве имён свойств объекта без "оборачивания" их в кавычки. Подробнее в <a href="http://es5.github.io/#x7.6.1">спецификации ES5</a>.</p>
</div>
<p>Больше информации об объектах и прототипах: <a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype">Object.prototype</a>.</p>
<p>Для получения информации по прототипам объектов и цепям прототипов объектов смотрите <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain">Inheritance and the prototype chain</a>.</p>
<div class="blockIndicator note">
<p>Начиная с ECMAScript 2015, ключи объектов могут быть определены переменными с использованием квадратных скобок при создании: <code>{[phoneType]: 12345}</code> допустимо вместо <code>var userPhone = {}; userPhone[phoneType] = 12345</code>.</p>
</div>
<h2 id="Массивы">Массивы</h2>
<p>Массивы в JavaScript всего лишь частный случай объектов. Работают они практически одинаково (если именем свойства является число, то доступ к нему можно получить только через вызов в скобках []), только у массивов есть одно удивительное свойство '<code>length</code>' (длина). Оно возвращает число, равное самому большому индексу массива + 1.</p>
<p>Создать массив можно по старинке:</p>
<pre class="brush: js notranslate">var a = new Array();
a[0] = "dog";
a[1] = "cat";
a[2] = "hen";
a.length; // 3
</pre>
<p>Но гораздо удобнее использовать литерал массива:</p>
<pre class="brush: js notranslate">var a = ["dog", "cat", "hen"];
a.length; // 3
</pre>
<p>Запомните, свойство <code>array.length</code> не обязательно будет показывать количество элементов в массиве. Посмотрите пример:</p>
<pre class="brush: js notranslate">var a = ["dog", "cat", "hen"];
a[100] = "fox";
a.length; // 101
</pre>
<p>Запомните — длина массива это его самый большой индекс плюс один.</p>
<p>Если попытаться получить доступ к несуществующему элементу массива, то получите <code>undefined</code>:</p>
<pre class="brush: js notranslate">typeof a[90]; // undefined
</pre>
<p>Для перебора элементов массива используйте такой способ:</p>
<pre class="brush: js notranslate">for (var i = 0; i < a.length; i++) {
// Сделать что-нибудь с элементом a[i]
}
</pre>
<p>ES2015 представляет более краткий <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of"><code>for</code>...<code>of</code></a> способ обхода по итерируемым объектам, в т.ч. массивам:</p>
<pre class="notranslate"><code>for (const currentValue of a) {
// С</code>делать что-нибудь с<code> currentValue
}</code>
</pre>
<p>Перебрать элементы массива также можно с помощью цикла <code><a href="/ru/docs/Web/JavaScript/Reference/Statements/for...in" title="en/Core_JavaScript_1.5_Reference/Statements/for...in">for...in</a></code>. Но, если вдруг будет изменено какое-либо свойство <code>Array.prototype</code>, то оно тоже будет участвовать в выборке. Не используйте данный метод.</p>
<p>И самый новый способ перебора свойств массива был добавлен в ECMAScript 5 — это метод <a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach">forEach()</a>:</p>
<pre class="brush: js notranslate">["dog", "cat", "hen"].forEach(function(currentValue, index, array) {
// Сделать что-нибудь с currentValue или array[index]
});</pre>
<p>Для добавления данных в массив используйте метод <code>push()</code>:</p>
<pre class="brush: js notranslate">a.push(item);</pre>
<p>У массивов есть ещё множество полезных методов. С их полным списком вы можете ознакомиться <a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/Array">по ссылке</a>.</p>
<table style="height: 124px; width: 598px;">
<thead>
<tr>
<th scope="col">Метод</th>
<th scope="col">Описание</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>a.toString()</code></td>
<td>Возвращает строковое представление массива, где все элементы разделены запятыми.</td>
</tr>
<tr>
<td><code>a.toLocaleString()</code></td>
<td>Возвращает строковое представление массива в соответствии с выбранной локалью.</td>
</tr>
<tr>
<td><code>a.concat(item1[, item2[, ...[, itemN]]])</code></td>
<td>Возвращает новый массив с добавлением указанных элементов.</td>
</tr>
<tr>
<td><code>a.join(sep)</code></td>
<td>Преобразует массив в строку, где в качестве разделителя используется параметр <code>sep</code></td>
</tr>
<tr>
<td><code>a.pop()</code></td>
<td>Удаляет последний элемент массива и возвращает его.</td>
</tr>
<tr>
<td><code>a.push(item1, ..., itemN)</code></td>
<td>Добавляет один или более элементов в конец массива.</td>
</tr>
<tr>
<td><code>a.reverse()</code></td>
<td>Меняет порядок элементов массива на обратный.</td>
</tr>
<tr>
<td><code>a.shift()</code></td>
<td>Удаляет первый элемент массива и возвращает его.</td>
</tr>
<tr>
<td><code>a.slice(start[, end])</code></td>
<td>Возвращает новый массив.</td>
</tr>
<tr>
<td><code>a.sort([cmpfn])</code></td>
<td>Сортирует данные в массиве.</td>
</tr>
<tr>
<td><code>a.splice(start, delcount[, item1[, ...[, itemN]]])</code></td>
<td>Позволяет вырезать из массива его часть и добавлять на это место новые элементы.</td>
</tr>
<tr>
<td><code>a.unshift(item1[, item2[, ...[, itemN]]])</code></td>
<td>Добавляет элементы в начало массива.</td>
</tr>
</tbody>
</table>
<h2 id="Функции">Функции</h2>
<p>Наряду с объектами функции также являются ключевыми компонентами языка JavaScript. Базовые функции очень просты:</p>
<pre class="brush: js notranslate">function add(x, y) {
var total = x + y;
return total;
}
</pre>
<p>В этом примере показано практически всё, что нужно знать о функциях. Функции в JavaScript могут принимать ноль или более параметров. Тело функции может содержать любые выражения и определять свои собственные переменные, которые будут для этой функции локальными. Инструкция <code>return</code> используется для возврата значения и остановки выполнения функции. Если инструкции return в функции нет (или есть, но не указано возвращаемое значение), то JavaScript возвратит <code>undefined</code>.</p>
<p>Можно вызвать функцию, вообще не передавая ей параметры. В таком случае будет считаться, что их значения равны <code>undefined</code>:</p>
<pre class="brush: js notranslate">add(); // NaN
// Нельзя проводить сложение undefined и undefined</pre>
<p>Можно передать больше аргументов, чем ожидает функция:</p>
<pre class="brush: js notranslate">add(2, 3, 4); // 5
// используются только первые два аргумента, "4" игнорируется</pre>
<p>Это может показаться бессмысленным, но на самом деле функции могут получить доступ к "лишним" аргументам с помощью псевдомассива <a href="/ru/docs/Web/JavaScript/Reference/Functions/arguments" title="En/Core_JavaScript_1.5_Reference/Functions_and_function_scope/arguments"><code>arguments</code></a>, в нём содержатся значения всех аргументов, переданных функции. Давайте напишем функцию, которая принимает неограниченное количество аргументов:</p>
<pre class="brush: js notranslate">function add() {
var sum = 0;
for (var i = 0, j = arguments.length; i < j; i++) {
sum += arguments[i];
}
return sum;
}
add(2, 3, 4, 5); // 14
</pre>
<p>Или создадим функцию для вычисления среднего значения:</p>
<pre class="brush: js notranslate">function avg() {
var sum = 0;
for (var i = 0, j = arguments.length; i < j; i++) {
sum += arguments[i];
}
return sum / arguments.length;
}
avg(2, 3, 4, 5); // 3.5
</pre>
<p>Это довольно полезно, но при этом кажется излишне подробным. Для уменьшения количества кода взглянем на замену использования массива аргументов способом <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters">Rest parameter syntax</a>. В этом случае мы можем передавать в функцию любое количество аргументов, сохраняя код минималистичным. <strong>Rest parameter operator</strong> используется в списке параметров функции в формате: <strong>...variable</strong> и включает в себя целый список аргументов, с которыми функция будет вызвана. Мы будем также использовать замену цикла <strong>for</strong> циклом <strong>for...of</strong> для получения значений, которые будет содержать наша переменная.</p>
<pre class="brush: js notranslate">function avg(...args) {
var sum = 0;
for (let value of args) {
sum += value;
}
return sum / args.length;
}
avg(2, 3, 4, 5); // 3.5</pre>
<div class="blockIndicator note">
<p>В вышенаписанном коде переменная <strong>args</strong> содержит все значения, которые были переданы в функцию.<br>
<br>
Важно отметить, что где бы ни был размещён <em>rest parameter operator</em> в объявлении функции, он будет содержать все аргументы <em>после</em> его объявления, не раньше. например: <em>function</em> <em>avg(</em><strong>firstValue, </strong><em>...args)</em><strong> </strong>будет хранить первое переданное значение в переменной <strong>firstValue </strong>и оставшиеся в <strong>args</strong>. Это ещё одно полезное свойство языка, однако оно ведёт нас к новой проблеме. <code>avg()</code> функция принимает список аргументов, разделённый запятыми. Но что если вы хотите найти среднее значение в массиве? Вы можете переписать функцию следующим образом:</p>
</div>
<pre class="brush: js notranslate">function avgArray(arr) {
var sum = 0;
for (var i = 0, j = arr.length; i < j; i++) {
sum += arr[i];
}
return sum / arr.length;
}
avgArray([2, 3, 4, 5]); // 3.5</pre>
<p>На тот случай, если вы хотите использовать первый вариант функции, а не переписывать её заново, то в JavaScript есть возможность вызывать функцию с произвольным массивом аргументов. Для этого используется метод <code><a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/Function/apply" title="en/Core_JavaScript_1.5_Reference/Global_Objects/Function/apply">apply()</a>:</code></p>
<pre class="brush: js notranslate">avg.apply(null, [2, 3, 4, 5]); // 3.5
</pre>
<p>Вторым аргументом метода <code>apply()</code> передаётся массив, который будет передан функции в качестве аргументов. О первом аргументе мы поговорим позже. Наличие у функций методов также говорит о том, что на самом деле они являются объектами.</p>
<div class="blockIndicator note">
<p>Этот же результат можно получить, используя <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator">spread operator</a> в вызове функции.</p>
<p>For instance: <code>avg(...numbers)</code></p>
</div>
<p>В JavaScript можно создавать анонимные функции:</p>
<pre class="brush: js notranslate">var avg = function() {
var sum = 0;
for (var i = 0, j = arguments.length; i < j; i++) {
sum += arguments[i];
}
return sum / arguments.length;
}
</pre>
<p>Данная запись семантически равнозначна записи <code>function</code> <code>avg()</code>. Это даёт возможность использовать разные интересные трюки. Вот посмотрите, как можно "спрятать" локальные переменные в функции:</p>
<pre class="brush: js notranslate">var a = 1;
var b = 2;
(function() {
var b = 3;
a += b;
})();
a; // 4
b; // 2
</pre>
<p>В JavaScript есть возможность рекурсивного вызова функции. Это может оказаться полезным при работе с иерархическими (древовидными) структурами данных (например такие, которые встречаются при работе с <a href="/ru/DOM" title="en/DOM">DOM</a>).</p>
<pre class="brush: js notranslate">function countChars(elm) {
if (elm.nodeType == 3) { // TEXT_NODE
return elm.nodeValue.length;
}
var count = 0;
for (var i = 0, child; child = elm.childNodes[i]; i++) {
count += countChars(child);
}
return count;
}
</pre>
<p>Тут мы сталкиваемся с проблемой: как вызвать функцию рекурсивно, если у неё нет имени? Для этого в JavaScript есть именованные функциональные выражения <a href="https://developer.mozilla.org/en-US/docs/Glossary/IIFE">IIFEs (Immediately Invoked Function Expressions)</a>. Вот пример использования именованной самовызывающейся функции:</p>
<pre class="brush: js notranslate">var charsInBody = (function counter(elm) {
if (elm.nodeType == 3) { // TEXT_NODE
return elm.nodeValue.length;
}
var count = 0;
for (var i = 0, child; child = elm.childNodes[i]; i++) {
count += counter(child);
}
return count;
})(document.body);
</pre>
<p>Имя функции в примере доступно только внутри самой функции. Это улучшает оптимизацию и читаемость кода.</p>
<h2 id="Собственные_объекты">Собственные объекты</h2>
<div class="note"><strong>На заметку:</strong> Для более подробной информации по объектно-ориентированному программированию в JavaScript смотрит <a href="/ru/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript" title="https://developer.mozilla.org/en/Introduction_to_Object-Oriented_JavaScript">Введение в объектно-ориентированный JavaScript</a>.</div>
<p>В классическом Объектно-Ориентированном Программировании (ООП) объекты — это коллекции данных и методов, которые этими данными оперируют. JavaScript - это язык, основанный на прототипах, и в его определении нет понятия классов, таких, как в языках C++ или Java. (Иногда это может запутать программистов, знакомых с языками, в которых есть классы.) Вместо классов JavaScript использует функции. Давайте представим объект с личными данными, содержащий поля с именем и фамилией. Есть два типа отображения имён: "Имя Фамилия" или "Фамилия, Имя". С помощью объектов и функций можно сделать следующее:</p>
<div class="blockIndicator warning">
<pre class="brush: js notranslate">function makePerson(first, last) {
return {
first: first,
last: last
}
}
function personFullName(person) {
return person.first + ' ' + person.last;
}
function personFullNameReversed(person) {
return person.last + ', ' + person.first
}
s = makePerson("Simon", "Willison");
personFullName(s); // Simon Willison
personFullNameReversed(s); // Willison, Simon
</pre>
</div>
<p>Работает, но сам код никуда не годится. С таким подходом у вас будут десятки функций, засоряющих глобальный объект. Это можно исправить, прикрепив функцию к объекту. Это просто, ведь все функции и есть объекты:</p>
<pre class="brush: js notranslate">function makePerson(first, last) {
return {
first: first,
last: last,
fullName: function() {
return this.first + ' ' + this.last;
},
fullNameReversed: function() {
return this.last + ', ' + this.first;
}
}
}
s = makePerson("Simon", "Willison")
s.fullName(); // Simon Willison
s.fullNameReversed(); // Willison, Simon
</pre>
<p>А вот кое-что новенькое: ключевое слово '<code><a href="/ru/docs/Web/JavaScript/Reference/Operators/this" title="en/Core_JavaScript_1.5_Reference/Operators/Special_Operators/this_Operator">this</a></code>'. Когда '<code>this</code>' используется внутри функции, оно ссылается на текущий объект. Значение ключевого слова зависит от способа вызова функции. Если вызвать функцию с обращением к объекту через <a href="/ru/docs/Web/JavaScript/Reference/Operators/Property_Accessors" title="en/Core_JavaScript_1.5_Reference/Operators/Member_Operators">точку или квадратные скобки</a>, то '<code>this</code>' получится равным данному объекту. В ином случае '<code>this</code>' будет ссылаться на глобальный объект. Это часто приводит к ошибкам. Например:</p>
<pre class="brush: js notranslate">s = makePerson("Simon", "Willison")
var fullName = s.fullName;
fullName(); // undefined undefined
</pre>
<p>При вызове <code>fullName()</code>, '<code>this</code>' получает ссылку на глобальный объект. А так как в глобальном объекте не определены переменные <code>first</code> и <code>last</code>, то имеем два <code>undefined</code>.</p>
<p>Используя особенность ключевого слова '<code>this</code>', можно улучшить код функции <code>makePerson</code>:</p>
<pre class="brush: js notranslate">function Person(first, last) {
this.first = first;
this.last = last;
this.fullName = function() {
return this.first + ' ' + this.last;
};
this.fullNameReversed = function() {
return this.last + ', ' + this.first;
};
}
var s = new Person("Simon", "Willison");
</pre>
<p>В примере мы использовали новое ключевое слово: '<code><a href="/ru/docs/Web/JavaScript/Reference/Operators/new" title="en/Core_JavaScript_1.5_Reference/Operators/Special_Operators/new_Operator">new</a></code>'. Оно тесно связано с '<a href="/ru/docs/Web/JavaScript/Reference/Operators/this">this</a>'. Данное ключевое слово создаёт новый пустой объект, а потом вызывает указанную функцию, а <code>this</code> получает ссылку на этот новый объект. Функции, которые предназначены для вызова с '<code>new</code>' называются конструкторами. Существует соглашение, согласно которому все функции-конструкторы записываются с заглавной буквы.</p>
<p>Мы доработали наш код в предыдущем примере, но всё равно остался один неприятный момент с самостоятельным вызовом <code>fullName()</code>.</p>
<p>Каждый раз, когда с помощью конструктора создаётся новый объект, мы заново создаём и две новые функции. Гораздо удобнее создать эти функции отдельно и дать доступ к ним конструктору:</p>
<pre class="brush: js notranslate">function personFullName() {
return this.first + ' ' + this.last;
}
function personFullNameReversed() {
return this.last + ', ' + this.first;
}
function Person(first, last) {
this.first = first;
this.last = last;
this.fullName = personFullName;
this.fullNameReversed = personFullNameReversed;
}
</pre>
<p>Уже лучше: мы создали функции-методы только один раз, а при новом вызове функции-конструктора просто ссылаемся на них. Можно сделать ещё лучше? Конечно:</p>
<pre class="brush: js notranslate">function Person(first, last) {
this.first = first;
this.last = last;
}
<code>Person.prototype.fullName = function fullName() {</code>
return this.first + ' ' + this.last;
}
<code>Person.prototype.fullNameReversed = function fullNameReversed() {</code>
return this.last + ', ' + this.first;
}
</pre>
<p><code>Person.prototype</code> это объект, доступ к которому есть у всех экземпляров класса <code>Person</code>. Он создаёт особую цепочку прототипов. Каждый раз, когда вы пытаетесь получить доступ к несуществующему свойству объекта <code>Person</code>, JavaScript проверяет, существует ли свойство в <code>Person.prototype</code>. В результате все, что передано в <code>Person.prototype</code>, становится доступным и всем экземплярам этого конструктора через <code>this</code> объект.</p>
<p>Это очень мощный инструмент. JavaScript позволяет изменять прототипы в любое время, это значит, что можно добавлять новые методы к существующим объектам во время выполнения программы:</p>
<pre class="brush: js notranslate">s = new Person("Simon", "Willison");
s.firstNameCaps();
// TypeError on line 1: s.firstNameCaps is not a function
Person.prototype.firstNameCaps = function() {
return this.first.toUpperCase()
}
s.firstNameCaps(); // "SIMON"
</pre>
<p>Занимательно то, что добавлять свойства в прототип можно и для встроенных объектов JavaScript. Давайте добавим новый метод <code>reversed </code>классу <code>String</code>, этот метод будет возвращать строку задом наперёд:</p>
<pre class="brush: js notranslate">var s = "Simon";
s.reversed()<code>; // TypeError on line 1: s.reversed is not a function</code>
String.prototype.reversed = function <code>reversed</code>() {
var r = "";
for (var i = this.length - 1; i >= 0; i--) {
r += this[i];
}
return r;
}
s.reversed(); // "nomiS"
</pre>
<p>Данный метод будет работать даже на литералах строки!</p>
<pre class="brush: js notranslate">"This can now be reversed".reversed();
// desrever eb won nac sihT
</pre>
<p>Как уже упоминалось, prototype формирует часть цепочки. Конечным объектом этой цепочки прототипов является <code>Object.prototype</code>, методы которого включают и <code>toString()</code> — тот метод, который вызывается тогда, когда надо получить строковое отображение объекта. Вот что можно сделать с нашими объектами <code>Person</code>:</p>
<pre class="brush: js notranslate">var s = new Person("Simon", "Willison");
s.toString(); // [object Object]
Person.prototype.toString = function() {
return '<Person: ' + this.fullName() + '>';
}
s.toString(); // "<Person: Simon Willison>"</pre>
<p>Помните, мы вызывали <code>avg.apply()</code> с первым аргументом равным null? Теперь мы можем сделать так: первым аргументом, переданным методу <code>apply()</code> будет объект, который примет значение '<code>this</code>'. Вот к примеру упрощённая реализация '<code>new</code>':</p>
<pre class="brush: js notranslate">function trivialNew(constructor, ...args) {
var o = {}; // Создаём новый объект
constructor.apply(o, args);
return o;
}
</pre>
<p>Это не точная копия <code>new</code>, так как она не устанавливает цепочку прототипов (это сложно ). Метод <code><a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/Function/apply">apply()</a></code> применяется не очень часто, но знать его важно. В примере выше, запись <code>...args</code> (включая многоточие) называется "<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters">rest arguments</a>"— она включает в себя все оставшиеся аргументы.</p>
<p>Вызов</p>
<pre class="brush: js notranslate">var bill = trivialNew(Person, 'William', 'Orange');</pre>
<p>практически полностью эквивалентен этому:</p>
<pre class="brush: js notranslate">var bill = new Person('William', 'Orange');</pre>
<p>В JavaScript метод <code><a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/Function/apply">apply()</a></code> имеет похожий метод <a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/Function/call" title="en/Core_JavaScript_1.5_Reference/Global_Objects/Function/call"><code>call()</code></a>, который тоже позволяет устанавливать '<code>this</code>', но принимает список, а не массив аргументов.</p>
<pre class="brush: js notranslate">function lastNameCaps() {
return this.last.toUpperCase();
}
var s = new Person("Simon", "Willison");
lastNameCaps.call(s);
// Аналогично записи:
s.lastNameCaps = lastNameCaps;
s.lastNameCaps(); <code>// WILLISON</code></pre>
<h2 id="Вложенные_функции">Вложенные функции</h2>
<p>Объявлять новые функции можно и внутри других функций. Мы использовали этот приём чуть выше, создавая функцию <code>makePerson()</code>. Главная особенность вложенных функций в том, что они получают доступ к переменным, объявленным в их функции-родителе:</p>
<pre class="brush: js notranslate">function parentFunc() {
var a = 1;
function nestedFunc() {
var b = 4; // parentFunc can't use this
return a + b;
}
return nestedFunc(); // 5
}</pre>
<p>Это очень полезное свойство, которое делает сопровождение кода более удобным. Если ваша функция в своей работе использует другие функции, которые больше нигде не используются, то можно просто вложить вспомогательные функции в основную. Это сократит количество функций в глобальном объекте, что довольно неплохо.</p>
<p>Ещё это отличный способ сократить количество глобальных переменных. Так при написании кода у нас часто будет возникать искушение понасоздавать глобальных переменных, которые будут доступны разным функциям. Всё это усложняет код, делает его менее читаемым. Вложенные функции имеют доступ к переменным своей функции-родителя, и мы можем использовать это для группировки множества функций вместе (естественно в разумных пределах), что позволит держать наш глобальный объект в чистоте и порядке.</p>
<h2 id="Замыкания_Closures"><a href="/ru/docs/Web/JavaScript/Closures">Замыкания</a> (Closures)</h2>
<p>Мы подошли к одному из самых мощных и непонятных инструментов JavaScript. Давайте разберёмся.</p>
<pre class="brush: js notranslate">function makeAdder(a) {
return function(b) {
return a + b;
};
}
var x = makeAdder(5);
var y = makeAdder(20);
x(6); // ?
y(7); // ?
</pre>
<p>Функция <code>makeAdder</code> создаёт новую функцию, которая прибавляет полученное значение к значению, которые было получено при создании функции.</p>
<p>Такой же фокус мы наблюдали в предыдущем примере, когда внутренние функции получали доступ к переменным той функции, в которой были объявлены. Только в нашем примере основная функция возвращает вложенную. Поначалу может показаться, что локальные переменные при этом перестанут существовать. Но они продолжают существовать — иначе код попросту не сработал бы. Вдобавок ко всему у нас есть две разные "копии" функции <code>makeAdder</code>, присвоенные разным переменным (одна копия, в которой <code>а</code> - это 5, а во второй <code>а</code> - это 20). Вот что имеем в результате вызова:</p>
<pre class="brush: js notranslate">x(6); // возвратит 11
y(7); // возвратит 27
</pre>
<p>И вот что произошло: когда JavaScript выполняет функцию, создаётся объект 'scope', который содержит в себе все локальные переменные, объявленные внутри этой функции. Он инициализируется любым значением, переданным функции в качестве параметра. 'Scope' подобен глобальному объекту, который содержит все глобальные переменные и функции, кроме нескольких важных отличий: при каждом вызове функции создаётся новый объект 'scope' и, в отличие от глобального, к объекту 'scope' нельзя получить прямой доступ из вашего кода. И нет способа пройтись по свойствам данного объекта.</p>
<p>Так что при вызове функции <code>makeAdder</code> создаётся новый объект 'scope' с единственным свойством: <code>a</code>, которому присваивается значение, переданное функции в качестве аргумента. Потом <code>makeAdder</code> возвращает новую анонимную функцию. В любом другом случае 'сборщик мусора' удалил бы объект scope, но возвращаемая функция ссылается на этот объект. В итоге объект scope не удаляется до тех пор, пока существует хотя бы одна ссылка на него.</p>
<p>Все объекты scope соединяются в цепочку областей видимости, которая похожа на цепочку прототипов в объектной системе JavaScript.</p>
<p><strong>Замыкание</strong> это связка из функции и объекта scope, созданного при её вызове. <a href="http://stackoverflow.com/questions/111102/how-do-javascript-closures-work">Подробнее о замыканиях здесь</a>.</p>
|