forked from BonsaiDen/JavaScript-Garden
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
1476 lines (1017 loc) · 78.9 KB
/
index.html
File metadata and controls
1476 lines (1017 loc) · 78.9 KB
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
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html><html lang="fi"><head><title>JavaScript-puutarha</title><meta charset="utf-8"><meta name="description" content="Opas JavaScriptin outouksiin ja vikoihin "><link rel="stylesheet" href="../style/garden.css" media="all"><link rel="stylesheet" href="../style/print.css" media="print"><!--[if lt IE 9]>
<script src="javascript/html5.js"></script>
<![endif]-->
</head><body><!-- Navigation--><nav id="nav_main"><div><ul><li><a href="/JavaScript-Garden/pl" title="JavaScript Garden - ogród JavaScript po polsku">pl</a></li><li><a href="/JavaScript-Garden/" title="JavaScript Garden in English">en</a></li><li><a href="/JavaScript-Garden/ru" title="JavaScript Гарден по-русски">ru</a></li><li><a href="/JavaScript-Garden/zh" title="JavaScript Garden 中文翻译">zh</a></li><li><a href="/JavaScript-Garden/tr" title="JavaScript Garden Türkçe">tr</a></li><li><a href="/JavaScript-Garden/es" title="JavaScript Garden es Español">es</a></li><li><a href="/JavaScript-Garden/ja" title="JavaScript Garden in Japanese">ja</a></li><li><a href="/JavaScript-Garden/ko" title="JavaScript Garden">ko</a></li><li class="active"><a href="/JavaScript-Garden/fi" title="JavaScript-puutarha suomeksi">fi</a></li></ul><a id="top" href="#intro" title="Back to top">#top</a><a id="hide_menu" class="tablet">Hide Menu</a></div><ul><li class="nav_intro"><h1><a href="#intro">Johdanto</a></h1><ul><li><a href="#intro.authors">Tekijät</a></li><li><a href="#intro.contributors">Osallistujat</a></li><li><a href="#intro.translators">Kääntäjät</a></li><li><a href="#intro.license">Lisenssi</a></li></ul></li><li class="nav_object"><h1><a href="#object">Oliot</a></h1><ul><li><a href="#object.general">Olioiden käyttö ja ominaisuudet</a></li><li><a href="#object.prototype">Prototyyppi</a></li><li><a href="#object.hasownproperty"><code>hasOwnProperty</code></a></li><li><a href="#object.forinloop"><code>for in</code>-luuppi</a></li></ul></li><li class="nav_function"><h1><a href="#function">Funktiot</a></h1><ul><li><a href="#function.general">Funktiomääreet ja lausekkeet</a></li><li><a href="#function.this">Kuinka <code>this</code> toimii</a></li><li><a href="#function.closures">Sulkeumat ja viitteet</a></li><li><a href="#function.arguments"><code>arguments</code>-olio</a></li><li><a href="#function.constructors">Konstruktorit</a></li><li><a href="#function.scopes">Näkyvyysalueet ja nimiavaruudet</a></li></ul></li><li class="nav_array"><h1><a href="#array">Taulukot</a></h1><ul><li><a href="#array.general">Taulukon iterointi ja attribuutit</a></li><li><a href="#array.constructor"><code>Array</code>-konstruktori</a></li></ul></li><li class="nav_types"><h1><a href="#types">Tyypit</a></h1><ul><li><a href="#types.equality">Yhtäsuuruus ja vertailut</a></li><li><a href="#types.typeof"><code>typeof</code>-operaattori</a></li><li><a href="#types.instanceof"><code>instanceof</code>-operaattori</a></li><li><a href="#types.casting">Tyyppimuunnokset</a></li></ul></li><li class="nav_core"><h1><a href="#core">Ydin</a></h1><ul><li><a href="#core.eval">Miksi <code>eval</code>-funktiota tulee välttää</a></li><li><a href="#core.undefined"><code>undefined</code> ja <code>null</code></a></li><li><a href="#core.semicolon">Automaattiset puolipisteet</a></li></ul></li><li class="nav_other"><h1><a href="#other">Muuta</a></h1><ul><li><a href="#other.timeouts"><code>setTimeout</code> ja <code>setInterval</code></a></li></ul></li></ul></nav><!-- Mobile navigation--><nav id="nav_mobile"><a id="nav_prev_section" href="#">prev section<span class="nav_section_name">section name</span></a><a id="nav_next_section" href="#">next section<span class="nav_section_name">section name</span></a><a id="show_menu">show menu</a></nav><!-- Sections--><section id="intro"><!-- Introduction--><header id="intro.intro"><h1>Johdanto</h1><div><p><strong>JavaScript-puutarha</strong> sisältää kasvavan kokoelman JavaScriptin ongelmallisiin osiin liittyvää dokumentaatiota. Se tarjoaa vinkkejä, joiden avulla välttää yleisiä virheitä, bugeja sekä suorituskykyongelmia ja huonoja tapoja, joita aloittelevat JavaScript-ohjelmoijat saattavat kohdata kieleen tutustuessaan.</p>
<p>JavaScript-puutarha <strong>ei</strong> tähtää itse kielen opettamiseen. On suositeltavaa, että lukija ymmärtää jo kielen perusteet ennen itse tekstin lukemista. Nämä perusteet voit oppia esimerkiksi perehtymällä Mozilla Developer Networkin erinomaiseen <a href="https://developer.mozilla.org/en/JavaScript/Guide">oppaaseen</a>.</p></div></header><!-- Articles--><article id="intro.authors"><h2>Tekijät</h2><div><p>Tämä opas pohjautuu kahden mukavan <a href="http://stackoverflow.com/">Stack Overflow</a> käyttäjän työhön. He ovat <a href="http://stackoverflow.com/users/170224/ivo-wetzel">Ivo Wetzel</a> (kirjoittaminen) sekä <a href="http://stackoverflow.com/users/313758/yi-jiang">Zhang Yi Jiang</a> (ulkoasu).</p></div></article><article id="intro.contributors"><h2>Osallistujat</h2><div><ul>
<li><a href="https://github.com/caio">Caio Romão</a> (oikeinkirjoituskorjauksia)</li>
<li><a href="https://github.com/blixt">Andreas Blixt</a> (kielikorjauksia)</li>
</ul></div></article><article id="intro.translators"><h2>Kääntäjät</h2><div><ul>
<li><a href="https://github.com/bebraw">Juho Vepsäläinen</a></li>
</ul></div></article><article id="intro.license"><h2>Lisenssi</h2><div><p>JavaScript-puutarha on julkaistu <a href="https://github.com/BonsaiDen/JavaScript-Garden/blob/next/LICENSE">MIT-lisenssin</a>-alaisena ja se on saatavilla <a href="https://github.com/BonsaiDen/JavaScript-Garden">GitHubissa</a>. Mikäli löydät virheitä, lisää se <a href="https://github.com/BonsaiDen/JavaScript-Garden/issues">seurantajärjestelmään</a> tai tee <code>pull</code>-pyyntö. Löydät meidät myös <a href="http://chat.stackoverflow.com/rooms/17/javascript">JavaScript huoneesta</a> Stack Overflown chatista.</p></div></article></section><section id="object"><!-- Introduction--><header id="object.intro"><h1>Oliot</h1></header><!-- Articles--><article id="object.general"><h2>Olioiden käyttö ja ominaisuudet</h2><div><p>Kaikki muuttujat, kahta poikkeusta lukuunottamatta, käyttäytyvät JavaScriptissä oliomaisesti. Nämä poikkeukset ovat <a href="#core.undefined"><code>null</code></a> sekä <a href="#core.undefined"><code>undefined</code></a>.</p>
<pre><code>false.toString() // epätosi
[1, 2, 3].toString(); // '1,2,3'
function Foo(){}
Foo.bar = 1;
Foo.bar; // 1
</code></pre>
<p>Yleisesti luullaan ettei numeroliteraaleja voida käyttää olioina. Tämä johtuu viasta JavaScriptin parserissa. Se yrittää parsia numeron <em>pistenotaatiota</em> liukulukuliteraalina.</p>
<pre><code>2.toString(); // palauttaa SyntaxError-virheen
</code></pre>
<p>Tämä voidaan välttää esimerkiksi seuraavasti.</p>
<pre><code>2..toString(); // toinen piste tunnistuu oikein
2 .toString(); // huomaa pisteen vasemmalla puolen oleva väli
(2).toString(); // 2 arvioidaan ensi
</code></pre>
</div><div><h3>Oliot tietotyyppinä</h3>
<p>JavaScriptin olioita voidaan käyttää myös <a href="http://en.wikipedia.org/wiki/Hashmap"><em>hajautustauluna</em></a>, koska ne muodostavat pääasiassa avaimien ja niihin liittyvien arvojen välisen mappauksen.</p>
<p>Olioliteraalinotaatiota - <code>{}</code> - käyttäen voidaan luoda tyhjä olio. Tämä olio <a href="#object.prototype">perii</a> <code>Object.prototype</code>-olion eikä sille ole määritelty <a href="#object.hasownproperty">omia ominaisuuksia</a>.</p>
<pre><code>var foo = {}; // uusi, tyhjä olio
// uusi, tyhjä olio, joka sisältää ominaisuuden 'test' arvolla 12
var bar = {test: 12};
</code></pre>
</div><div><h3>Pääsy ominaisuuksiin</h3>
<p>Olion ominaisuuksiin voidaan päästä käsiksi kahta eri tapaa käyttäen. Siihen voidaan käyttää joko piste- tai hakasulkunotaatiota.</p>
<pre><code>var foo = {name: 'Kitten'}
foo.name; // kitten
foo['name']; // kitten
var get = 'name';
foo[get]; // kitten
foo.1234; // SyntaxError
foo['1234']; // toimii
</code></pre>
<p>Kumpikin notaatio toimii samalla tavoin. Ainut ero liittyy siihen, että hakasulkunotaation avulla ominaisuuksien arvoja voidaan asettaa dynaamisesti. Se sallii myös muuten hankalien, virheeseen johtavien nimien käyttämisen.</p>
</div><div><h3>Ominaisuuksien poistaminen</h3>
<p>Ainut tapa poistaa olion ominaisuus on käyttää <code>delete</code>-operaattoria. Ominaisuuden asettaminen joko arvoon <code>undefined</code> tai <code>null</code> poistaa vain siihen liittyneen arvon muttei itse <em>avainta</em>.</p>
<pre><code>var obj = {
bar: 1,
foo: 2,
baz: 3
};
obj.bar = undefined;
obj.foo = null;
delete obj.baz;
for(var i in obj) {
if (obj.hasOwnProperty(i)) {
console.log(i, '' + obj[i]);
}
}
</code></pre>
<p>Yllä oleva koodi tulostaa sekä <code>both undefined</code> että <code>foo null</code>. Ainoastaan <code>baz</code> on poistettu. Täten sitä ei myöskään näy tulosteessa.</p>
</div><div><h3>Avainnotaatio</h3>
<pre><code>var test = {
'case': 'Olen avainsana, joten minun tulee olla merkkijono',
delete: 'Myös minä olen avainsana' // palauttaa SyntaxError-virheen
};
</code></pre>
<p>Olioiden ominaisuuksia voidaan notatoida käyttäen joko pelkkiä merkkejä tai merkkijonoja. Toisesta JavaScriptin suunnitteluvirheestä johtuen yllä oleva koodi palauttaa <code>SyntaxError</code>-virheen ECMAScript 5:ttä edeltävissä versioissa.</p>
<p>Tämä virhe johtuu siitä, että <code>delete</code> on <em>avainsana</em>. Täten se tulee notatoida <em>merkkijonona</em>. Tällöin myös vanhemmat JavaScript-tulkit ymmärtävät sen oikein.</p></div></article><article id="object.prototype"><h2>Prototyyppi</h2><div><p>JavaScript ei sisällä klassista perintämallia. Sen sijaan se käyttää <em>prototyyppeihin</em> pohjautuvaa ratkaisua.</p>
<p>Usein tätä pidetään JavaScriptin eräänä suurimmista heikkouksista. Itse asiassa prototyyppipohjainen perintämalli on voimakkaampi kuin klassinen malli. Sen avulla voidaan mallintaa klassinen malli melko helposti. Toisin päin mallintaminen on huomattavasti vaikeampaa.</p>
<p>JavaScript on käytännössä ainut laajasti käytetty kieli, joka tarjoaa tuen prototyyppipohjaiselle perinnälle. Tästä johtuen mallien väliseen eroon tottuminen voi viedä jonkin akaa.</p>
<p>Ensimmäinen suuri ero liittyy siihen, kuinka perintä toimii. JavaScriptissä se pohjautuu erityisiin <em>prototyyppiketjuihin</em>.</p>
<aside>
<p><strong>Huomio:</strong> Ainoastaan <code>Bar.prototype = Foo.prototype</code> johtaa siihen, että molemmat oliot jakavat <strong>saman</strong> prototyypin. Tällöin olioiden prototyyppeihin tehdyt muutokset heijastuvat siis molempiin. Usein tämä ei ole itse tarkoitus.</p>
</aside>
<pre><code>function Foo() {
this.value = 42;
}
Foo.prototype = {
method: function() {}
};
function Bar() {}
// Aseta Barin prototyypin uuteen Foo-olioon
Bar.prototype = new Foo();
Bar.prototype.foo = 'Terve maailma';
// Huolehdi siitä, että Bar on todellinen konstruktori
Bar.prototype.constructor = Bar;
var test = new Bar() // luo uusi bar
// Prototyyppiketju
test [Bar-olio]
Bar.prototype [Foo-olio]
{ foo: 'Terve maailma' }
Foo.prototype
{ method: ... }
Object.prototype
{ toString: ... /* jne. */ }
</code></pre>
<p>Yllä olio <code>test</code> perii sekä <code>Bar.prototype</code>- että <code>Foo.prototype</code>-olion. Tällöin se pääsee käsiksi <code>Foo</code>:ssa määriteltyy funktioon <code>method</code>. Se pääsee käsiksi myös ominaisuuteen <code>value</code>, jonka luotu <code>Foo</code>-olio sisältää prototyypissään. On tärkeää huomata, että <code>new Bar()</code> <strong>ei</strong> luo uutta <code>Foo</code>-oliota vaan käyttää uudelleen sen prototyyppiin asetettua. Tässä tapauksessa kaikki <code>Bar</code>-oliot jakavat siis <strong>saman</strong> <code>value</code>-ominaisuuden.</p>
<aside>
<p><strong>Huomio:</strong> <strong>Älä</strong> käytä <code>Bar.prototype = Foo</code>-notaatiota. Tässä tapauksessa se ei osoita <code>Foo</code>n prototyyppiin vaan funktio-olioon <code>Foo</code>. Tällöin prototyyppiketju osoittaa itse asiassa <code>Function.prototype</code>-olioon eikä <code>Foo.prototype</code>-olioon, kuten oli tarkoitus. <code>method</code> ei siis tällöin olisi mukana prototyyppiketjussa.</p>
</aside>
</div><div><h3>Ominaisuushaut</h3>
<p>Kun olion ominaisuuksien arvoa haetaan, JavaScript käy prototyyppiketjua läpi <strong>ylöspäin</strong>, kunnes se löytää ominaisuuden nimeä vastaavan arvon.</p>
<p>Jos se saavuttaa ketjun huipun - <code>Object.prototype</code>-olion - eikä ole vieläkään löytänyt haettua ominaisuutta, se palauttaa <a href="#core.undefined">undefined</a> arvon sen sijaan.</p>
</div><div><h3>Prototyyppi-ominaisuus</h3>
<p>Vaikka Prototyyppi-ominaisuutta käytetään prototyyppiketjujen rakentamiseen, voidaan siihen asettaa <strong>mikä tahansa</strong> arvo. Mikäli arvo on primitiivi, se yksinkertaisesti jätetään huomiotta.</p>
<pre><code>function Foo() {}
Foo.prototype = 1; // ei vaikutusta
</code></pre>
<p>Kuten esimerkissä yllä, prototyyppiin on mahdollista asettaa olioita. Tällä tavoin prototyyppiketjuja voidaan koostaa dynaamisesti.</p>
</div><div><h3>Suorituskyky</h3>
<p>Prototyyppiketjussa korkealla olevien ominaisuuksien hakeminen voi hidastaa koodin kriittisiä osia. Tämän lisäksi olemattomien ominaisuuksien hakeminen käy koko ketjun läpi.</p>
<p>Ominaisuuksia <a href="#object.forinloop">iteroidessa</a> prototyyppiketjun <strong>jokainen</strong> ominaisuus käydään läpi.</p>
</div><div><h3>Natiivien prototyyppien laajentaminen</h3>
<p>JavaScript mahdollistaa <code>Object.prototype</code>-olion sekä muiden natiivityyppien laajentamisen.</p>
<p>Tätä tekniikkaa kutsutaan nimellä <a href="http://en.wikipedia.org/wiki/Monkey_patch">apinapätsäämiseksi</a>. Se rikkoo <em>kapseloinnin. Vaikka yleisesti käytetyt alustat, kuten <a href="http://prototypejs.org/">Prototype</a>, käyttävätkin sitä, ei ole olemassa yhtään hyvää syytä, minkä takia natiivityyppejä tulisi laajentaa *epästandardilla</em> toiminnallisuudella.</p>
<p><strong>Ainut</strong> hyvä syy on uudempien JavaScript-tulkkien sisältämien ominaisuuksien siirtäminen vanhemmille alustoille. Eräs esimerkki tästä on <a href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach"><code>Array.forEach</code></a>.</p>
</div><div><h3>Yhteenveto</h3>
<p>Ennen kuin kirjoitat monimutkaista prototyyppiperintää hyödyntävää koodia, on <strong>olennaista</strong>, että ymmärrät täysin kuinka se toimii. Ota huomioon myös prototyyppiketjujen pituus ja riko niitä tarpeen mukaan välttääksesi suorituskykyongelmia. Huomioi myös, että natiiveja prototyyppejä ei tule laajentaa <strong>milloinkaan</strong> ellei kyse ole vain yhteensopivuudesta uudempien JavaScript-ominaisuuksien kanssa.</p></div></article><article id="object.hasownproperty"><h2><code>hasOwnProperty</code></h2><div><p>Jotta voimme tarkistaa onko olion ominaisuus määritelty siinä <em>itsessään</em>, tulee käyttää erityistä <code>Object.prototype</code>-oliosta periytyvää <code>hasOwnProperty</code>-metodia. Tällä tavoin vältämme <a href="#object.prototype">prototyyppiketjun</a> sisältämät ominaisuudet.</p>
<aside>
<p><strong>Huomio:</strong> <strong>Ei</strong> riitä tarkistaa vain että ominaisuuden arvo on <code>undefined</code>. Ominaisuus voi hyvinkin olla olemassa. Sen arvoksi on vain satuttu asettamaan <code>undefined</code>.</p>
</aside>
<p><code>hasOwnProperty</code> on ainut JavaScriptin sisältämä metodi, joka käsittelee ominaisuuksia <strong>eikä</strong> käy prototyyppiketjun sisältöä läpi.</p>
<pre><code>// Object.prototypen myrkyttäminen
Object.prototype.bar = 1;
var foo = {goo: undefined};
foo.bar; // 1
'bar' in foo; // tosi
foo.hasOwnProperty('bar'); // epätosi
foo.hasOwnProperty('goo'); // tosi
</code></pre>
<p>Ainoastaan <code>hasOwnProperty</code> palauttaa oikean ja odotetun tuloksen. Sen tietäminen on olennaista minkä tahansa olion ominaisuuksia iteroidessa. Tämä on <strong>ainut</strong> tapa löytää olion itsensä ominaisuudet prototyyppiketjusta riippumatta.</p>
</div><div><h3><code>hasOwnProperty</code> ominaisuutena</h3>
<p>JavaScript <strong>ei</strong> suojele <code>hasOwnProperty</code>-metodin nimeä. Täten on mahdollista, että olio voi sisältää samannimisen ominaisuuden. Jotta voimme saada oikeita tuloksia, tulee sen sijaan käyttää <em>ulkoista</em> <code>hasOwnProperty</code>-metodia.</p>
<pre><code>var foo = {
hasOwnProperty: function() {
return false;
},
bar: 'Olkoon vaikka lohikäärmeitä'
};
foo.hasOwnProperty('bar'); // palauttaa aina epätoden
// Käytä toisen olion hasOwnProperty-metodia ja kutsu sitä asettamalla
// 'this' foohon
({}).hasOwnProperty.call(foo, 'bar'); // tosi
</code></pre>
</div><div><h3>Yhteenveto</h3>
<p>Mikäli pitää selvittää kuuluuko ominaisuus olioon vai ei, <strong>ainoastaan</strong> <code>hasOwnProperty</code> voi kertoa sen. Tämän lisäksi on suositeltavaa käyttää <code>hasOwnProperty</code>-metodia osana <strong>jokaista</strong> <a href="#object.forinloop"><code>for in</code>-luuppia</a>. Tällä tavoin voidaan välttää natiivien <a href="#object.prototype">prototyyppien</a> laajentamiseen liittyviä ongelmia.</p></div></article><article id="object.forinloop"><h2><code>for in</code>-luuppi</h2><div><p>Aivan kuten <code>in</code>-operaattori, myös <code>for in</code>-luuppi käy olion prototyyppiketjun läpi iteroidessaan sen ominaisuuksia.</p>
<aside>
<p><strong>Huomio:</strong> <code>for in</code>-luuppi <strong>ei</strong> iteroi ominaisuuksia, joiden <code>enumerable</code>-attribuutti on asetettu arvoon <code>false</code>. Eräs esimerkki tästä on taulukon <code>length</code>-ominaisuus.</p>
</aside>
<pre><code>// Object.prototypen myrkyttäminen
Object.prototype.bar = 1;
var foo = {moo: 2};
for(var i in foo) {
console.log(i); // tulostaa sekä bar että moo
}
</code></pre>
<p>Koska <code>for in</code>-luupin käytöstapaa ei voida muokata suoraan, tulee ei-halutut ominaisuudet karsia itse luupin sisällä. Tämä on mahdollista käyttäen <code>Object.prototype</code>-olion <a href="#object.hasownproperty"><code>hasOwnProperty</code></a>-metodia.</p>
<aside>
<p><strong>Huomio:</strong> <code>for in</code>-luupin suorittaminen hidastuu sitä enemmän, mitä pidempi olion prototyyppiketju on. Tämä johtuu siitä, että se joutuu käymään koko ketjun sisällön läpi.</p>
</aside>
</div><div><h3><code>hasOwnProperty</code>-metodin käyttäminen karsimiseen</h3>
<pre><code>// foo kuten yllä
for(var i in foo) {
if (foo.hasOwnProperty(i)) {
console.log(i);
}
}
</code></pre>
<p>Tämä versio on ainut oikea. Se tulostaa <strong>ainoastaan</strong> <code>moo</code>, koska se käyttää <code>hasOwnProperty</code>-metodia oikein. Kun se jätetään pois, on koodi altis virheille tapauksissa, joissa prototyyppejä, kuten <code>Object.prototype</code>, on laajennettu.</p>
<p><a href="http://www.prototypejs.org/">Prototype</a> on eräs yleisesti käytetty ohjelmointialusta, joka tekee näin. Kun kyseistä alustaa käytetään, <code>for in</code>-luupit, jotka eivät käytä <code>hasOwnProperty</code>-metodia, menevät varmasti rikki.</p>
</div><div><h3>Yhteenveto</h3>
<p>On suositeltavaa käyttää <strong>aina</strong> <code>hasOwnProperty</code>-metodia. Ei ole kannattavaa tehdä ajoympäristöön tai prototyyppeihin liittyviä oletuksia.</p></div></article></section><section id="function"><!-- Introduction--><header id="function.intro"><h1>Funktiot</h1></header><!-- Articles--><article id="function.general"><h2>Funktiomääreet ja lausekkeet</h2><div><p>JavaScriptissä funktiot ovat ensimmäisen luokan olioita. Tämä tarkoittaa sitä, että niitä voidaan välittää kuten muitakin arvoja. Usein tätä käytetään takaisinkutsuissa käyttämällä <em>nimettömiä, mahdollisesti asynkronisia funktioita</em>. </p>
</div><div><h3><code>function</code>-määre</h3>
<pre><code>function foo() {}
</code></pre>
<p>Yllä oleva funktio <a href="#function.scopes">hilataan</a> ennen ohjelman suorituksen alkua. Se näkyy <em>kaikkialle</em> näkyvyysalueessaan, jossa se on <em>määritelty</em>. Tämä on totta jopa silloin, jos sitä kutsutaan ennen määrittelyään.</p>
<pre><code>foo(); // Toimii, koska foo on luotu ennen kuin koodi suoritetaan
function foo() {}
</code></pre>
</div><div><h3><code>function</code>-lauseke</h3>
<pre><code>var foo = function() {};
</code></pre>
<p>Tämä esimerkki asettaa nimeämättömän ja <em>nimettömän</em> funktion muuttujan <code>foo</code> arvoksi.</p>
<pre><code>foo; // 'undefined'
foo(); // tämä palauttaa TypeError-virheen
var foo = function() {};
</code></pre>
<p><code>var</code> on määre. Tästä johtuen se hilaa muuttujanimen <code>foo</code> ennen kuin itse koodia ryhdytään suorittamaan.</p>
<p>Sijoituslauseet suoritetaan <em>vasta</em> kun niihin saavutaan. Tästä johtuen <code>foo</code> saa arvokseen <a href="#core.undefined">undefined</a> ennen kuin varsinaista sijoitusta päästään suorittamaan.</p>
</div><div><h3>Nimetty funktiolauseke</h3>
<p>Nimettyjen funktioiden sijoitus tarjoaa toisen erikoistapauksen.</p>
<pre><code>var foo = function bar() {
bar(); // Toimii
}
bar(); // ReferenceError
</code></pre>
<p>Tässä tapauksessa <code>bar</code> ei ole saatavilla ulommalla näkyvyysalueessa. Tämä johtuu siitä, että se on sidottu <code>foo</code>:n sisälle. Tämä johtuu siitä, kuinka näkyvyysalueet ja niihin kuuluvat jäsenet <a href="#function.scopes">tulkitaan</a>. Funktion nimi on <em>aina</em> saatavilla sen paikallisessa näkyvyysalueessa itsessään.</p></div></article><article id="function.this"><h2>Kuinka <code>this</code> toimii</h2><div><p>JavaScripting <code>this</code> toimii eri tavoin kuin useimmissa kielissä. Tarkalleen ottaen on olemassa <strong>viisi</strong> eri tapaa, joiden mukaan sen arvo voi määrittyä.</p>
</div><div><h3>Globaali näkyvyysalue</h3>
<pre><code>this;
</code></pre>
<p>Kun <code>this</code>-muuttujaa käytetään globaalissa näkyvyysalueessa, viittaa se <em>globaaliin</em> olioon.</p>
</div><div><h3>Funktiokutsu</h3>
<pre><code>foo();
</code></pre>
<p>Tässä tapauksessa <code>this</code> viittaa jälleen <em>globaaliin</em> olioon.</p>
<aside class="es5"><p><strong>ES5 Huomio:</strong> Globaalia tapausta ei ole <strong>enää</strong> olemassa, kun käytetään tiukkaa moodia. Sen sijaan <code>this</code> saa arvon <code>undefined</code>.</p>
</aside>
</div><div><h3>Metodikutsu</h3>
<pre><code>test.foo();
</code></pre>
<p>Tässä esimerkissä <code>this</code> viittaa <code>test</code>-olioon.</p>
</div><div><h3>Konstruktorikutsu</h3>
<pre><code>new foo();
</code></pre>
<p>Funktiokutsu, jota edeltää <code>new</code>-avainsana toimii <a href="#function.constructors">konstruktorina</a>. Funktion sisällä <code>this</code> viittaa <em>juuri luotuun</em> <code>Object</code>-olioon.</p>
</div><div><h3><code>this</code>-arvon asettaminen</h3>
<pre><code>function foo(a, b, c) {}
var bar = {};
foo.apply(bar, [1, 2, 3]); // taulukko laajenee alla olevaksi
foo.call(bar, 1, 2, 3); // tuloksena a = 1, b = 2, c = 3
</code></pre>
<p><code>Function.prototype</code>-olion <code>call</code>- ja <code>apply</code>-metodeita käytettäessä <code>this</code>-ominaisuuden arvo määrittyy ensimmäisen annetun argumentin perusteella.</p>
<p>Seurauksena <code>foo</code>-funktion sisältämä <code>this</code> asettuu <code>bar</code>-olioon toisin kuin perustapauksessa.</p>
<aside>
<p><strong>Huomio:</strong> <code>this</code> <strong>ei voi</strong> viitata <code>Object</code>-literaalin sisältämään olioon. Tästä seuraa, että <code>var obj = {me: this}</code> tapauksessa <code>me</code> <strong>ei</strong> viittaa <code>obj</code>-olioon. <code>this</code>-arvo määrittyy ainoastaan listatuissa viidessä tapauksessa.</p>
</aside>
</div><div><h3>Yleisiä ongelmakohtia</h3>
<p>Useimmat näistä tapauksista ovat järkeviä. Ensimmäistä niistä tosin voidaan pitää suunnitteluvirheenä, jolle ei ole mitään järkevää käyttöä <strong>ikinä</strong>.</p>
<pre><code>Foo.method = function() {
function test() {
// this asettuu globaaliin olioon
}
test();
}
</code></pre>
<p>Yleisesti luullaan, että test-funktion sisältämä <code>this</code> viittaa tässä tapauksessa <code>Foo</code>-olioon. Todellisuudessa se <strong>ei</strong> kuitenkaan tee näin.</p>
<p>Jotta <code>Foo</code>-olioon voidaan päästä käsiksi <code>test</code>-funktion sisällä, tulee metodin sisälle luoda paikallinen muuttuja, joka viittaa <code>Foo</code>-olioon.</p>
<pre><code>Foo.method = function() {
var that = this;
function test() {
// Käytä thatia thissin sijasta
}
test();
}
</code></pre>
<p><code>that</code> on normaali nimi, jota käytetään yleisesti viittaamaan ulompaan <code>this</code>-muuttujaan. <a href="#function.closures">Sulkeumia</a> käytettäessä <code>this</code>-arvoa voidaan myös välittää edelleen.</p>
</div><div><h3>Metodien sijoittaminen</h3>
<p>JavaScriptissä funktioita <strong>ei</strong> voida nimetä uudelleen eli siis sijoittaa <strong>edelleen</strong>.</p>
<pre><code>var test = someObject.methodTest;
test();
</code></pre>
<p>Ensimmäisestä tapauksesta johtuen <code>test</code> toimii kuten normaali funktiokutsu; tällöin sen sisältämä <code>this</code> ei enää osoita <code>someObject</code>-olioon.</p>
<p>Vaikka <code>this</code>-arvon myöhäinen sidonta saattaa vaikuttaa huonolta idealta, se mahdollistaa <a href="#object.prototype">prototyyppeihin pohjautuvan perinnän</a>.</p>
<pre><code>function Foo() {}
Foo.prototype.method = function() {};
function Bar() {}
Bar.prototype = Foo.prototype;
new Bar().method();
</code></pre>
<p>Kun <code>method</code>-metodia kutsutaan <code>Bar</code>-oliossa, sen <code>this</code> viittaa juurikin tuohon olioon.</p></div></article><article id="function.closures"><h2>Sulkeumat ja viitteet</h2><div><p><em>Sulkeumat</em> ovat eräs JavaScriptin voimakkaimmista ominaisuuksista. Näkyvyysalueilla on siis <strong>aina</strong> pääsy ulompaan näkyvyysalueeseensa. Koska JavaScriptissä ainut tapa määritellä näkyvyyttä pohjautuu <a href="#function.scopes">funktionäkyvyyteen</a>, kaikki funktiot käyttäytyvät oletuksena sulkeumina.</p>
</div><div><h3>Paikallisten muuttujien emulointi</h3>
<pre><code>function Counter(start) {
var count = start;
return {
increment: function() {
count++;
},
get: function() {
return count;
}
}
}
var foo = Counter(4);
foo.increment();
foo.get(); // 5
</code></pre>
<p>Tässä tapauksessa <code>Counter</code> palauttaa <strong>kaksi</strong> sulkeumaa. Funktion <code>increment</code> lisäksi palautetaan myös funktio <code>get</code>. Kumpikin funktio <strong>viittaa</strong> <code>Counter</code>-näkyvyysalueeseen ja pääsee siten käsiksi <code>count</code>-muuttujan arvoon.</p>
</div><div><h3>Miksi paikalliset muuttujat toimivat</h3>
<p>JavaScriptissä ei voida viitata näkyvyysalueisiin. Tästä seuraa <strong>ettei</strong> <code>count</code>-muuttujan arvoon voida päästä käsiksi funktion ulkopuolelta. Ainoastaan nämä kaksi sulkeumaa mahdollistavat sen.</p>
<pre><code>var foo = new Counter(4);
foo.hack = function() {
count = 1337;
};
</code></pre>
<p>Yllä oleva koodi <strong>ei</strong> muuta muuttujan <code>count</code> arvoa <code>Counter</code>-näkyvyysalueessa. Tämä johtuu siitä, että <code>foo.hack</code>-ominaisuutta ei ole määritelty <strong>kyseisessä</strong> näkyvyysalueessa. Sen sijaan se luo - tai ylikirjoittaa - <em>globaalin</em> muuttujan <code>count</code>.</p>
</div><div><h3>Sulkeumat luupeissa</h3>
<p>Usein sulkeumia käytetään väärin luuppien sisällä indeksimuuttujien arvon kopiointiin.</p>
<pre><code>for(var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
</code></pre>
<p>Yllä oleva koodi <strong>ei</strong> tulosta numeroita <code>nollasta</code> <code>yhdeksään</code>. Sen sijaan se tulostaa numeron <code>10</code> kymmenen kertaa.</p>
<p><em>Nimetön</em> funktio saa <strong>viitteen</strong> <code>i</code>-muuttujaan <code>console.log</code>-kutsuhetkellä. Tällöin luuppi on jo suoritettu ja <code>i</code>:n arvoksi on asetettu <code>10</code>.</p>
<p>Päästäksemme haluttuun lopputulokseen on tarpeen luoda <strong>kopio</strong> <code>i</code>:n arvosta.</p>
</div><div><h3>Viiteongelman välttäminen</h3>
<p>Voimme välttää ongelman käyttämällä <a href="#function.scopes">nimetöntä käärettä</a>.</p>
<pre><code>for(var i = 0; i < 10; i++) {
(function(e) {
setTimeout(function() {
console.log(e);
}, 1000);
})(i);
}
</code></pre>
<p>Nimetöntä ulkofunktiota kutsutaan heti käyttäen <code>i</code>:tä se ensimmäisenä argumenttina. Tällöin se saa kopion <code>i</code>:n <strong>arvosta</strong> parametrina <code>e</code>.</p>
<p>Nimetön funktio, jolle annetaan <code>setTimeout</code> sisältää nyt viitteen <code>e</code>:hen, jonka arvoa luuppi <strong>ei</strong> muuta.</p>
<p>Samaan lopputulokseen voidaan päästä myös palauttamalla funktio nimettömästä kääreestä. Tällöin se käyttäytyy samoin kuten yllä.</p>
<pre><code>for(var i = 0; i < 10; i++) {
setTimeout((function(e) {
return function() {
console.log(e);
}
})(i), 1000)
}
</code></pre></div></article><article id="function.arguments"><h2><code>arguments</code>-olio</h2><div><p>Jokainen JavaScriptin näkyvyysalue pääsee käsiksi erikoismuuttujaan nimeltään <code>arguments</code>. Tämä muuttuja sisältää listan kaikista funktiolle annetuista argumenteista.</p>
<aside>
<p><strong>Huomio:</strong> Mikäli <code>arguments</code> on jo määritelty funktion sisällä joko näkyvyysalueen, <code>var</code>-lauseen tai parametrin kautta, <code>arguments</code>-oliota ei luoda.</p>
</aside>
<p><code>arguments</code>-olio <strong>ei</strong> ole <code>Array</code>. Sen semantiikka, erityisesti <code>length</code>-ominaisuus, muistuttaa taulukkoa. Tästä huolimatta se ei peri <code>Array.prototype</code>:stä ja on itse asiassa <code>Object</code>.</p>
<p>Tästä johtuen <code>arguments</code>-olioon <strong>ei</strong> voida soveltaa normaaleja taulukkometodeja, kuten <code>push</code>, <code>pop</code> tai <code>slice</code>. Vaikka iterointi onnistuukin <code>for</code>-luuppeja käyttäen, tulee se muuttaa aidoksi <code>Array</code>-olioksi ennen kuin siihen voidaan soveltaa näitä metodeja.</p>
</div><div><h3>Array-olioksi muuttaminen</h3>
<p>Alla oleva koodi palauttaa uuden <code>Array</code>-olion, joka sisältää <code>arguments</code>-olion kaikki jäsenet.</p>
<pre><code>Array.prototype.slice.call(arguments);
</code></pre>
<p>Tämä muutos on luonteeltaan <strong>hidas</strong> eikä sitä suositella käytettävän suorituskykyä vaativissa osissa koodia.</p>
</div><div><h3>Argumenttien antaminen</h3>
<p>Funktiosta toiselle voidaan antaa argumentteja seuraavasti.</p>
<pre><code>function foo() {
bar.apply(null, arguments);
}
function bar(a, b, c) {
// tee jotain
}
</code></pre>
<p>Toinen keino on käyttää sekä <code>call</code>- että <code>apply</code>-funktioita yhdessä ja luoda nopeita, sitomattomia kääreitä.</p>
<pre><code>function Foo() {}
Foo.prototype.method = function(a, b, c) {
console.log(this, a, b, c);
};
// Luo "metodin" sitomaton versio
// Se ottaa seuraavat parametrit: this, arg1, arg2...argN
Foo.method = function() {
// Tulos: Foo.prototype.method.call(this, arg1, arg2... argN)
Function.call.apply(Foo.prototype.method, arguments);
};
</code></pre>
</div><div><h3>Muodolliset parametrit ja argumenttien indeksit</h3>
<p><code>arguments</code>-olio luo sekä <em>getter</em>- että <em>setter</em>-funktiot sekä sen ominaisuuksille että myös funktion muodollisille parametreille.</p>
<p>Tästä seuraa, että muodollisen parametrin arvon muuttaminen muuttaa myös <code>arguments</code>-olion vastaavan ominaisuuden arvoa ja toisin päin.</p>
<pre><code>function foo(a, b, c) {
arguments[0] = 2;
a; // 2
b = 4;
arguments[1]; // 4
var d = c;
d = 9;
c; // 3
}
foo(1, 2, 3);
</code></pre>
</div><div><h3>Suorituskykyyn liittyviä myyttejä ja totuuksia</h3>
<p><code>arguments</code>-olio luodaan aina paitsi jos se on jo julistettu nimenä funktiossa tai sen muodollisena parametrina. Tämä siitä huolimatta käytetäänkö sitä vai ei.</p>
<p>Sekä <em>getter</em>- ja <em>setter</em>-funktiot luodaan <strong>aina</strong>. Tästä seuraa, että niiden käytöllä ei ole juurikaan merkitystä suorituskyvyn kannalta.</p>
<aside class="es5"><p><strong>ES5 Huomio:</strong> Näitä <em>getter</em>- ja <em>setter</em>-funktioita ei luoda tiukassa moodissa.</p>
</aside>
<p>On kuitenkin eräs tapaus, jossa suorituskyky kärsii. Tämä liittyy <code>arguments.callee</code>-ominaisuuden käyttöön.</p>
<pre><code>function foo() {
arguments.callee; // tee jotain tällä funktio-oliolla
arguments.callee.caller; // ja kutsuvalla funktio-oliolla
}
function bigLoop() {
for(var i = 0; i < 100000; i++) {
foo(); // normaalisti tämä olisi inline-optimoitu
}
}
</code></pre>
<p>Yllä olevassa koodissa <code>foo</code>-kutsua ei voida <a href="http://en.wikipedia.org/wiki/Inlining">käsitellä avoimesti</a>, koska sen tulee tietää sekä itsestään että kutsujasta. Sen lisäksi, että se haittaa suorituskykyä, rikkoo se myös kapseloinnin. Tässä tapauksessa funktio voi olla riippuvainen tietystä kutsuympäristöstä.</p>
<p>On <strong>erittäin suositeltavaa</strong> ettei <code>arguments.callee</code>-ominaisuutta tai sen ominaisuuksia käytetä <strong>ikinä</strong>.</p>
<aside class="es5"><p><strong>ES5 Huomio:</strong> Tiukassa moodissa <code>arguments.callee</code> palauttaa <code>TypeError</code>-virheen, koska se käyttö on vanhennettu.</p>
</aside></div></article><article id="function.constructors"><h2>Konstruktorit</h2><div><p>JavaScriptin konstruktorit eroavat monista muista kielistä selvästi. Jokainen funktiokutsu, joka sisältää avainsanan <code>new</code> toimii konstruktorina.</p>
<p>Konstruktorin - kutsutun funktion - <code>this</code>-muuttujan arvo viittaa luotuun <code>Object</code>-olioon. Tämän <strong>uuden</strong> olion <a href="#object.prototype"><code>prototyyppi</code></a> asetetaan osoittamaan konstruktorin kutsuman funktio-olion prototyyppiin.</p>
<p>Mikäli kutsuttu funktio ei sisällä selvää <code>return</code>-lausetta, tällöin se palauttaa <code>this</code>-muuttujan arvon eli uuden olion.</p>
<pre><code>function Foo() {
this.bla = 1;
}
Foo.prototype.test = function() {
console.log(this.bla);
};
var test = new Foo();
</code></pre>
<p>Yllä <code>Foo</code>:ta kutsutaan konstruktorina. Juuri luodun olion <code>prototyyppi</code> asetetaan osoittamaan ominaisuuteen <code>Foo.prototype</code>.</p>
<p>Selvän <code>return</code>-lausekkeen tapauksessa funktio palauttaa ainoastaan määritellyn lausekkeen arvon. Tämä pätee tosin <strong>vain jos</strong> palautettava arvo on tyypiltään <code>Object</code>.</p>
<pre><code>function Bar() {
return 2;
}
new Bar(); // uusi olio
function Test() {
this.value = 2;
return {
foo: 1
};
}
new Test(); // palautettu olio
</code></pre>
<p>Mikäli <code>new</code>-avainsanaa ei käytetä, funktio <strong>ei</strong> palauta uutta oliota.</p>
<pre><code>function Foo() {
this.bla = 1; // asetetaan globaalisti
}
Foo(); // undefined
</code></pre>
<p>Vaikka yllä oleva esimerkki saattaa näyttää toimivan joissain tapauksissa, viittaa <a href="#function.this"><code>this</code></a> globaalin olion <code>this</code>-ominaisuuteen.</p>
</div><div><h3>Tehtaat</h3>
<p>Mikäli <code>new</code>-avainsanan käyttöä halutaan välttää, voidaan konstruktori pakottaa palauttamaan arvo.</p>
<pre><code>function Bar() {
var value = 1;
return {
method: function() {
return value;
}
}
}
Bar.prototype = {
foo: function() {}
};
new Bar();
Bar();
</code></pre>
<p>Tässä tapauksessa molemmat <code>Bar</code>-funktion kutsut käyttäytyvät samoin. Kumpikin kutsu palauttaa olion, joka sisältää <code>method</code>-ominaisuuden. Kyseinen ominaisuus on <a href="#function.closures">sulkeuma</a>.</p>
<p>On myös tärkeää huomata, että kutsu <code>new Bar()</code> <strong>ei</strong> vaikuta palautetun olion prototyyppiin. Vaikka luodun olion prototyyppi onkin asetettu, <code>Bar</code> ei palauta ikinä kyseistä prototyyppioliota.</p>
<p>Yllä olevassa esimerkissä <code>new</code>-avainsanan käytöllä tai käyttämällä jättämisellä ei ole toiminnan kannalta mitään merkitystä.</p>
</div><div><h3>Tehtaiden käyttö uusien olioiden luomiseen</h3>
<p>Usein suositellaan <code>new</code>-avainsanan käytön <strong>välttämistä</strong>. Tämä johtuu siitä, että sen käyttämättä jättäminen voi johtaa bugeihin.</p>
<p>Sen sijaan suositellaan käytettävän tehdasta, jonka sisällä varsinainen olio konstruoidaan.</p>
<pre><code>function Foo() {
var obj = {};
obj.value = 'blub';
var private = 2;
obj.someMethod = function(value) {
this.value = value;
}
obj.getPrivate = function() {
return private;
}
return obj;
}
</code></pre>
<p>Vaikka yllä oleva esimerkki välttää <code>new</code>-avainsanan käyttöä ja tekee <a href="#function.closures">paikallisten muuttujien</a> käytön helpommaksi, sisältää se joitain huonoja puolia. </p>
<ol>
<li>Se käyttää enemmän muistia. Tämä johtuu siitä, että luodut oliot <strong>eivät</strong> jaa prototyypin metodeja.</li>
<li>Perinnän tapauksessa tehtaan tulee kopioida toisen olion kaikki metodit tai vaihtoehtoisesti asettaa kyseinen olio toisen prototyypiksi.</li>
<li>Prototyyppiketjun käsitteen unohtaminen on vain välttääksemme <code>new</code>-avainsanan käyttöä on vastoin kielen filosofista perustaa.</li>
</ol>
</div><div><h3>Yhteenveto</h3>
<p>Vaikka <code>new</code>-avainsanan käyttö voi johtaa bugeihin, prototyyppien käyttöä <strong>ei</strong> kannata unohtaa kokonaan. Loppujen lopuksi kyse on siitä, kumpi tapa sopii sovelluksen tarpeisiin paremmin. On erityisen tärkeää valita jokin tietty tapa ja <strong>pitäytyä</strong> sen käytössä.</p></div></article><article id="function.scopes"><h2>Näkyvyysalueet ja nimiavaruudet</h2><div><p>Vaikka JavaScript-käyttääkin aaltosulkeita blokkien ilmaisuun, se <strong>ei</strong> tue blokkinäkyvyyttä. Tämä tarkoittaa sitä, että kieli tukee ainoastaan *funktionäkyvyyttä.</p>
<pre><code>function test() { // näkyvyysalue
for(var i = 0; i < 10; i++) { // tämä ei ole näkyvyysalue
// count
}
console.log(i); // 10
}
</code></pre>
<aside>
<p><strong>Huomio:</strong> Mikäli <code>return</code>-lausetta ei käytetä sijoitukseen, <code>{...}</code>-notaatio tulkitaan blokkina <strong>eikä</strong> olioliteraalina. Tästä ja [puolipisteiden automaattisesta lisäämisestä](#core.semicolon] seuraa yllättäviä virheitä.</p>
</aside>
<p>JavaScript ei myöskään sisällä erityistä tukea nimiavaruuksille. Tämä tarkoittaa sitä, että kaikki määritellään oletuksena <em>globaalissa</em> nimiavaruudessa.</p>
<p>Joka kerta kun muuttujaan viitataan, JavaScript käy kaikki näkyvyysalueet läpi alhaalta lähtien. Mikäli se saavuttaa globaalin näkyvyystalueen, eikä löydä haettua nimeä, se palauttaa <code>ReferenceError</code>-virheen.</p>
</div><div><h3>Riesa nimeltä globaalit muuttujat</h3>
<pre><code>// skripti A
foo = '42';
// skripti B
var foo = '42'
</code></pre>
<p>Yllä olevat skriptit käyttäytyvät <strong>eri</strong> tavoin. Skripti A määrittelee muuttujan nimeltä <code>foo</code> <em>globaalissa</em> näkyvyysalueessa. Skripti B määrittelee <code>foo</code>-muuttujan <em>vallitsevassa</em> näkyvyysalueessa.</p>
<p>Tämä <strong>ei</strong> ole <strong>sama asia</strong>. <code>var</code>-avainsanan käyttämättä jättäminen voi johtaa vakaviin seurauksiin.</p>
<pre><code>// globaali näkyvyysalue
var foo = 42;
function test() {
// paikallinen näkyvyysalue
foo = 21;
}
test();
foo; // 21
</code></pre>
<p><code>var</code>-avainsanan pois jättäminen johtaa siihen, että funktio <code>test</code> ylikirjoittaa <code>foo</code>:n arvon. Vaikka tämä ei välttämättä vaikutakaan suurelta asialta, tuhansien rivien tapauksessa <code>var</code>-avainsanan käyttämättömyys voi johtaa vaikeasti löydettäviin bugeihin.</p>
<pre><code>// globaali näkyvyysalue
var items = [/* joku lista */];
for(var i = 0; i < 10; i++) {
subLoop();
}
function subLoop() {
// aliluupin näkyvyysalue
for(i = 0; i < 10; i++) { // hups, var jäi pois
// jotain makeaa ja hienoa
}
}
</code></pre>
<p>Tässä tapauksessa ulomman luupin suoritus lopetetaan ensimmäisen <code>subLoop</code>-kutsun jälkeen. Tämä johtuu siitä, että se ylikirjoittaa <code>i</code>:n globaalin arvon. Mikäli jälkimmäisessä luupissa olisi käytetty <code>var</code>-avainsanaa, olisi ikävyyksiltä vältytty. <code>var</code>-avainsanaa ei siis tule <strong>ikinä</strong> jättää pois ellei siihen ole <em>hyvää syytä</em>.</p>
</div><div><h3>Paikalliset muuttujat</h3>
<p>Ainoastaan <a href="#function.general">funktion</a> parametrit ja muuttujat, jotka sisältävät <code>var</code>-määreen ovat paikallisia.</p>
<pre><code>// globaali näkyvyysalue
var foo = 1;
var bar = 2;
var i = 2;
function test(i) {
// paikallinen näkyvyysalue
i = 5;
var foo = 3;
bar = 4;
}
test(10);
</code></pre>
<p><code>foo</code> ja <code>i</code> ovatkin <code>test</code>-funktiolle paikallisia. <code>bar</code> sijoitus muuttaa globaalin muuttujan arvoa.</p>
</div><div><h3>Hilaaminen</h3>
<p>JavaScript <strong>hilaa</strong> määreitä. Tämä tarkoittaa sitä, että sekä <code>var</code>-lausekkeet että <code>function</code>-määreet siirretään ne sisältävän näkyvyysalueen huipulle.</p>
<pre><code>bar();
var bar = function() {};
var someValue = 42;
test();
function test(data) {
if (false) {
goo = 1;
} else {
var goo = 2;
}
for(var i = 0; i < 100; i++) {
var e = data[i];
}
}
</code></pre>
<p>Yllä olevaa koodia muutetaan ennen suoritusta. JavaScript siirtää <code>var</code>-lausekkeet ja <code>function</code>-määreet lähimmän näkyvyysalueen huipulle.</p>
<pre><code>// var-lausekkeet siirrettiin tänne
var bar, someValue; // oletuksena 'undefined'
// myös funktio-määre siirtyi tänne
function test(data) {
var goo, i, e; // ei blokkinäkyvyyttä, siirretään siis tänne
if (false) {
goo = 1;
} else {
goo = 2;
}
for(i = 0; i < 100; i++) {
e = data[i];
}
}
bar(); // TypeError-virhe, baria ei ole vielä määritelty
someValue = 42; // hilaus ei koske sijoituksia
bar = function() {};
test();
</code></pre>
<p>Sen lisäksi, että puuttuva blokkinäkyvyys siirtää <code>var</code>-lausekkeet luuppien ulkopuolelle, tekee se myös eräistä <code>if</code>-rakenteista vaikeita käsittää.</p>
<p>Alkuperäisessä koodissa <code>if</code>-lause näytti muokkaavan <em>globaalia muuttujaa</em> <code>goo</code>. Todellisuudessa se muokkaa <em>paikallista muuttujaa</em> varsinaisen hilauksen jälkeen.</p>
<p>Seuraava koodi saattaisi ensi näkemältä aiheuttaa <code>ReferenceError</code>-virheen. Näin ei kuitenkaan tapahdu <em>hilauksen</em> ansiosta.</p>
<pre><code>// onko SomeImportantThing alustettu
if (!SomeImportantThing) {
var SomeImportantThing = {};
}
</code></pre>
<p>Tämä toimii, koska <code>var</code>-lauseke on hilattu <em>globaalin näkyvyysalueen</em> huipulle.</p>
<pre><code>var SomeImportantThing;
// mahdollista alustuskoodia
// onhan se alustettu
if (!SomeImportantThing) {
SomeImportantThing = {};
}
</code></pre>
</div><div><h3>Nimienerottelujärjestys</h3>
<p>Kaikki JavaScriptin näkyvyysalueet, <em>globaalin näkyvyysalue</em> mukaanlukien, sisältävät erikoismuuttujan <a href="#function.this"><code>this</code></a>. <code>this</code> viittaa <em>tämänhetkiseen olioon</em>.</p>
<p>Funktioiden näkyvyysalueet sisältävät myös <a href="#function.arguments"><code>arguments</code></a>-olion. Se sisältää funktiolle annetut argumentit.</p>
<p>Mikäli näkyvyysalueen sisällä pyritään pääsemään käsiksi esimerkiksi <code>foo</code>:n arvoon JavaScript käyttäytyy seuraavasti:</p>
<ol>
<li>Mikäli <code>var foo</code>-lauseke löytyy tämänhetkisestä näkyvyysalueesta, käytä sen arvoa.</li>
<li>Mikäli eräs funktion parametreista on <code>foo</code>, käytä sitä.</li>
<li>Mikäli funktion nimi itsessään on <code>foo</code>, käytä sitä.</li>
<li>Siirry ulompaan näkyvyysalueeseen ja suorita <strong>#1</strong> uudelleen.</li>
</ol>
<aside>
<p><strong>Huomio:</strong> Mikäli funktio sisältää <code>arguments</code>-nimisen parametrin, estää se <code>arguments</code>-olion luonnin kokonaan.</p>
</aside>
</div><div><h3>Nimiavaruudet</h3>
<p>Globaalin nimiavaruuden ongelmana voidaan pitää nimitörmäyksiä. JavaScriptissä tätä ongelmaa voidaan kiertää käyttämällä <em>nimettömiä kääreitä</em>.</p>
<pre><code>(function() {
// "nimiavaruus" itsessään
window.foo = function() {
// paljastettu sulkeuma
};
})(); // suorita funktio heti
</code></pre>
<p>Nimettömiä funktioita pidetään <a href="#function.general">lauseina</a>. Jotta niitä voidaan kutsua, tulee ne suorittaa ensin.</p>
<pre><code>( // suorita sulkeiden sisältämä funktio
function() {}
) // ja palauta funktio-olio
() // kutsu suorituksen tulosta
</code></pre>
<p>Samaan lopputulokseen voidaan päästä myös hieman eri syntaksia käyttäen.</p>
<pre><code>// Kaksi muuta tapaa
+function(){}();
(function(){}());
</code></pre>
</div><div><h3>Yhteenveto</h3>
<p>On suositeltavaa käyttää <em>nimettömiä kääreitä</em> nimiavaruuksina. Sen lisäksi, että se suojelee koodia nimitörmäyksiltä, se tarjoaa keinon jaotella ohjelma paremmin.</p>
<p>Globaalien muuttujien käyttöä pidetään yleisesti <strong>huonona tapana</strong>. <strong>Mikä tahansa</strong> niiden käyttö viittaa huonosti kirjoitettuun, virheille alttiiseen ja hankalasti ylläpidettävään koodiin.</p></div></article></section><section id="array"><!-- Introduction--><header id="array.intro"><h1>Taulukot</h1></header><!-- Articles--><article id="array.general"><h2>Taulukon iterointi ja attribuutit</h2><div><p>Vaikka taulukot ovatkin JavaScript-olioita, niiden tapauksessa ei välttämättä kannata käyttää <a href="#object.forinloop"><code>for in loop</code></a>-luuppia. Pikemminkin tätä tapaa tulee <strong>välttää</strong>.</p>
<aside>
<p><strong>Huomio:</strong> JavaScript-taulukot <strong>eivät ole</strong> <em>assosiatiivisia</em>. JavaScriptissa ainoastaan <a href="#object.general">oliot</a> ovat avain-arvo-mappauksia. On huomattavaa, että toisin kuin assosiatiiviset taulukot, oliot <strong>eivät</strong> säilytä järjestystään.</p>
</aside>
<p><code>for in</code>-luuppi iteroi kaikki prototyyppiketjun sisältämät ominaisuudet. Tämän vuoksi tulee käyttää erityistä <a href="#object.hasownproperty"><code>hasOwnProperty</code></a>-metodia, jonka avulla voidaan taata, että käsitellään oikeita ominaisuuksia. Tästä johtuen iteroint on jo lähtökohtaisesti jopa <strong>kaksikymmentä</strong> kertaa hitaampaa kuin normaalin <code>for</code>-luupin tapauksessa.</p>
</div><div><h3>Iterointi</h3>
<p>Taulukkojen tapauksessa paras suorituskyky voidaan saavuttaa käyttämällä klassista <code>for</code>-luuppia.</p>
<pre><code>var list = [1, 2, 3, 4, 5, ...... 100000000];
for(var i = 0, l = list.length; i < l; i++) {
console.log(list[i]);
}
</code></pre>
<p>Edelliseen esimerkkiin liittyy yksi mutta. Listan pituus on tallennettu välimuistiin erikseen käyttämällä <code>l = list.length</code>-lauseketta.</p>
<p>Vaikka <code>length</code>-ominaisuus määritelläänkin taulukossa itsessään, arvon hakeminen sisältää ylimääräisen operaation. Uudehkot JavaScript-ympäristöt <strong>saattavat</strong> optimoida tämän tapauksen. Tästä ei kuitenkaan ole mitään takeita.</p>
<p>Todellisuudessa välimuistin käytön pois jättäminen voi hidastaa luuppia jopa puolella.</p>
</div><div><h3><code>length</code>-ominaisuus</h3>
<p><code>length</code>-ominaisuuden <em>getteri</em> palauttaa yksinkertaisesti taulukon sisältämien alkioiden määrän. Sen <em>setteriä</em> voidaan käyttää taulukon <strong>typistämiseen</strong>.</p>
<pre><code>var foo = [1, 2, 3, 4, 5, 6];
foo.length = 3;
foo; // [1, 2, 3]
foo.length = 6;
foo; // [1, 2, 3]
</code></pre>
<p>Pituuden pienemmäksi asettaminen typistää taulukkoa. Sen kasvattaminen ei kuitenkaan vaikuta mitenkään.</p>
</div><div><h3>Yhteenveto</h3>
<p>Parhaan suorituskyvyn kannalta on parhainta käyttää tavallista <code>for</code>-luuppia ja tallentaa <code>length</code>-ominaisuus välimuistiin. <code>for in</code>-luupin käyttö taulukon tapauksessa on merkki huonosti kirjoitetusta koodista, joka on altis bugeille ja heikolle suorituskyvylle.</p></div></article><article id="array.constructor"><h2><code>Array</code>-konstruktori</h2><div><p><code>Array</code>-oletuskonstruktorin käytös ei ole lainkaan yksiselitteistä. Tämän vuoksi suositellaankin, että konstruktorin sijasta käytetään literaalinotaatiota <code>[]</code>.</p>
<pre><code>[1, 2, 3]; // Tulos: [1, 2, 3]
new Array(1, 2, 3); // Tulos: [1, 2, 3]
[3]; // Tulos: [3]
new Array(3); // Tulos: []
new Array('3') // Tulos: ['3']
</code></pre>
<p>Mikäli <code>Array</code>-konstruktorille annetaan vain yksi argumentti ja se on tyypiltään <code>Number</code>, konstruktori palauttaa uuden <em>harvan</em> taulukon, jonka <code>length</code>-attribuutti on asetettu annetun numeron mukaisesti. On tärkeää huomata, että <strong>ainoastaan</strong> <code>length</code> asetetaan tällä tavoin, todellisia taulukon indeksejä ei alusteta.</p>
<pre><code>var arr = new Array(3);
arr[1]; // undefined
1 in arr; // false, indeksiä ei ole alustettu
</code></pre>
<p>Tämä on käytännöllistä vain harvoin, kuten merkkijonon toiston tapauksessa. Tällöin voidaan välttää <code>for-luupin</code> käyttämistä.</p>
<pre><code>new Array(count + 1).join(stringToRepeat);
</code></pre>
</div><div><h3>Yhteenveto</h3>
<p><code>Array</code>-konstruktorin käyttöä tulee käyttää niin paljon kuin suinkin mahdollista. Sen sijaan on suositeltavaa käyttää literaalinotaatiota. Literaalit ovat lyhyempiä ja niiden syntaksi on selkeämpi. Tämän lisäksi ne tekevät koodista luettavampaa.</p></div></article></section><section id="types"><!-- Introduction--><header id="types.intro"><h1>Tyypit</h1></header><!-- Articles--><article id="types.equality"><h2>Yhtäsuuruus ja vertailut</h2><div><p>JavaScript sisältää kaksi erilaista tapaa, joiden avulla olioiden arvoa voidaan verrata toisiinsa.</p>
</div><div><h3>Yhtäsuuruusoperaattori</h3>
<p>Yhtäsuuruusoperaattori koostuu kahdesta yhtäsuuruusmerkistä: <code>==</code></p>
<p>JavaScript tyypittyy <em>heikosti</em>. Tämä tarkoittaa sitä, että yhtäsuuruusoperaattori <strong>muuttaa</strong> tyyppejä verratakseen niitä keskenään.</p>
<pre><code>"" == "0" // epätosi
0 == "" // tosi
0 == "0" // tosi
false == "false" // epätosi
false == "0" // tosi
false == undefined // epätosi
false == null // epätosi
null == undefined // tosi
" \t\r\n" == 0 // tosi
</code></pre>
<p>Yllä oleva taulukko näyttää tyyppimuunnoksen tulokset. Tämä onkin eräs pääsyistä, minkä vuoksi <code>==</code>-operaattorin käyttöä pidetään huonona asiana. Sen käyttö johtaa hankalasti löydettäviin bugeihin monimutkaisista muunnossäännöistä johtuen.</p>
<p>Tämän lisäksi tyyppimuunnos vaikuttaa suorituskykyyn. Esimerkiksi merkkijono tulee muuttaa numeroksi ennen kuin sitä voidaan verrata toiseen numeroon.</p>
</div><div><h3>Tiukka yhtäsuuruusoperaattori</h3>
<p>Tiukka yhtäsuuruusoperaattori koostuu <strong>kolmesta</strong> yhtäsuuruusmerkistä: <code>===</code></p>
<p>Se toimii aivan kuten normaali yhtäsuuruusoperaattori. Se <strong>ei</strong> tosin tee minkäänlaista tyyppimuunnosta ennen vertailua.</p>
<pre><code>"" === "0" // epätosi
0 === "" // epätosi
0 === "0" // epätosi
false === "false" // epätosi
false === "0" // epätosi
false === undefined // epätosi
false === null // epätosi
null === undefined // epätosi
" \t\r\n" === 0 // epätosi
</code></pre>
<p>Yllä olevat tulokset ovat huomattavasti selkeämpiä ja mahdollistavat koodin menemisen rikki ajoissa. Tämä kovettaa koodia ja tarjoaa myös parempaa suorituskykyä siinä tapauksessa, että operandit ovat erityyppisiä.</p>
</div><div><h3>Olioiden vertailu</h3>
<p>Vaikka sekä <code>==</code> ja <code>===</code> ovat <strong>yhtäsuuruusoperaattoreita</strong>, ne toimivat eri tavoin, kun ainakin yksi operandeista sattuu olemaan <code>Object</code>.</p>
<pre><code>{} === {}; // epätosi
new String('foo') === 'foo'; // epätosi
new Number(10) === 10; // epätosi
var foo = {};
foo === foo; // tosi
</code></pre>
<p>Tässä tapauksessa molemmat operaattorit vertaavat olion <strong>identiteettiä</strong> <strong>eikä</strong> sen arvoa. Tämä tarkoittaa sitä, että vertailu tehdään olion <strong>instanssin</strong> tasolla aivan, kuten Pythonin <code>is</code>-operaattorin tai C:n osoitinvertailun tapauksessa.</p>
</div><div><h3>Yhteenveto</h3>
<p>On erittäin suositeltavaa, että ainoastaan <strong>tiukkaa yhtäsuuruusoperaattoria</strong> käytetään. Mikäli tyyppejä tulee muuttaa, tämä kannattaa tehdä <a href="#types.casting">selvästi</a> sen sijaan että luottaisi kielen monimutkaisiin muunnossääntöihin.</p></div></article><article id="types.typeof"><h2><code>typeof</code>-operaattori</h2><div><p><code>typeof</code>-operaattori, kuten myös <a href="#types.instanceof"><code>instanceof</code></a>, on kenties JavaScriptin suurin suunnitteluvirhe. Tämä johtuu siitä, että nämä ominaisuudet ovat liki kokonaan käyttökelvottomia.</p>
<p>Vaikka <code>instanceof</code>-operaattorilla onkin tiettyjä rajattuja käyttötarkoituksia, <code>typeof</code>-operaattorille on olemassa vain yksi käytännöllinen käyttötapaus, joka <strong>ei</strong> tapahdu olion tyyppiä tarkasteltaessa.</p>
<aside>
<p><strong>Huomio:</strong> Vaikka <code>typeof</code>-operaattoria voidaankin kutsua funktiomaisesti (<code>typeof(obj)</code>), ei tämä ole todellinen funktiokutsu. Sulut käyttäytyvät normaalisti ja niiden palauttamaa arvoa käytetään <code>typeof</code>-operaattorin operandina. <code>typeof</code>-funktiota <strong>ei</strong> ole olemassa.</p>
</aside>
</div><div><h3>JavaScriptin tyyppitaulukko</h3>