
Jak widzisz, tematem dzisiejszego wpisu są pętle JavaScript oraz dobre praktyki z nimi związane. Na temat pętli w języku JavaScript pisałem już co nieco w czasie moich przygotowań do egzaminu 70-480, a konkretnie w artykule “Sterowanie przepływem operacji w języku JavaScript”. Było to jednak dość ogólne potraktowanie tematu, a dziś chciałbym przyjrzeć się temu bliżej i pokazać jak korzystać z tego elementu języka w sposób jak najbardziej wydajny. W niniejszym poście przyjrzymy się przede wszystkim dwie pętle JavaScript: “for” oraz “for-in”, które są najczęściej stosowane.

Pętle JavaScript - dobre praktyki
Jak widzisz, tematem dzisiejszego wpisu są pętle JavaScript oraz dobre praktyki z nimi związane. Na temat pętli w języku JavaScript pisałem już co nieco w czasie moich przygotowań do egzaminu 70-480, a konkretnie w artykule “Sterowanie przepływem operacji w języku JavaScript”. Było to jednak dość ogólne potraktowanie tematu, a dziś chciałbym przyjrzeć się temu bliżej i pokazać jak korzystać z tego elementu języka w sposób jak najbardziej wydajny. W niniejszym poście przyjrzymy się przede wszystkim dwie pętle JavaScript: “for” oraz “for-in”, które są najczęściej stosowane.
Pętla for
Opisywana pętla służy przede wszystkim do iterowania po elementach tablic (lub obiektów przypominających tablice czyli kolekcji). Najbardziej rozpowszechnionym sposobem definiowania pętli “for” znanym też z innych języków programowania jest ten widoczny poniżej:
var testArray = [ '1', '2', '3' ];
for (var i = 0; i < testArray.length; i++){
alert(testArray[i]);
}
W przypadku tak prostego przykładu może to nie mieć znaczenia ale dla tak napisanej pętli jak w powyższym kodzie, wartość “length” tablicy pobierana jest przy każdym przebiegu pętli. Rozważmy więc drugi, bardziej “życiowy” przykład:
var paragraphs = document.getElementsByTagName('p');
for (var i = 0; i < paragraphs.length; i++){
alert(paragraphs[i].innerHTML);
}
Tutaj nie mamy już do czynienia ze zwykłą tablicą a z obiektem HTMLCollection. W przypadku tego rodzaju obiektów każdorazowe odwołanie do właściwości “length” skutkuje odwołanie się do struktury dokumentu DOM co jest już dużo bardziej kosztowne. W związku z tym, pierwszą dobrą praktyką jest trzymanie długości tablicy w osobnej zmiennej:
for (var i = 0, max = paragraphs.length; i < max; i++){
alert(paragraphs[i].innerHTML);
}
Jeśli nie odpowiada nam definiowanie dwóch zmiennych na potrzeby pętli, możemy zamiast tego zastosować iterowanie odwrotne, od wartości maksymalnej do zera:
for (var i = paragraphs.length; i--;){
alert(paragraphs[i].innerHTML);
}
Oczywiście, ten sam efekt możemy uzyskać za pomocą pętli while:
var paragraphs = document.getElementsByTagName('p'),
i = paragraphs.length;
while (i--) {
alert(paragraphs[i].innerHTML);
}
Należy pamiętać, że przy użyciu dwóch ostatnich przykładów, jako że iteracja odbywa się od tyłu, odwołania do elementów tablicy/kolekcji również odbywają się od tyłu. W związku z tym można je stosować w zależności od konkretnej sytuacji.
Inna sprawa, że narzędzie JSLint zaleca stosowanie zamiast “i++” oraz “i–” wyrażeń w stylu “i = i + 1” albo “i += 1”, jak dla mnie jednak, stosowanie “i++” jest na tyle rozpowszechnione wśród programistów, że każdy wie o co chodzi i nie jest to coś co wprowadzałby niepotrzebną nieczytelność.
Pętla for-in
Drugi rodzaj JavaScript’owych pętli, zwanych też wyliczeniami stosuje się do iteracji po właściwościach obiektów. Rozpocznijmy od przykładu:
var testObject = {
first: 1,
second: 2,
third: 3
}
for (var prop in testObject) {
alert(testObject[prop]);
}
Jak widać, funkcja tego rodzaju jest trochę podobna do pętli “foreach” z innych języków programowania, z tym że w każdej iteracji zwracana jest nazwa danej właściwości a nie jej wartość. Dlatego w linii ósmej, chcąc odwołać się do danej wartości, stosowana jest konstrukcja “testObject[prop]” - “prop” zawiera nazwę właściwości.
Ok, wszystko pięknie, tylko że powyższy kod został przygotowany na obsługę tak zdefiniowanego obiektu. A co jeśli ktoś gdzieś rozszerzy obiekt “Object” o dodatkową funkcję sumującą?:
Object.prototype.sum = function () {
// sum code
};
Rozwiązaniem tego problemu (i jednocześnie ogólnie dobrą praktyką podczas pisania pętli “for-in”) jest dodanie do pętli poniższego sprawdzenia:
for (var prop in testObject) {
if (testObject.hasOwnProperty(prop)) {
alert(testObject[prop]);
}
}
W opisywanym przypadku, użycie metody “hasOwnProperty” pozwoli na odfiltrowanie wszystkich właściwości (lub funkcji), które nie należą bezpośrednio do obiektu “testObject” (ponieważ zwykle nie chcemy by były one uwzględnione w tego rodzaju pętlach).
Oczywiście jest to tylko dobra praktyka i nie zawsze należy ją stosować. Na pewno istnieją przypadki gdy chcemy w pętli “for-in” pracować na wszystkich właściwościach danego obiektu. Zwykle jednak zależy nam tylko na właściwościach należących bezpośrednio do danego obiektu, a wówczas opisywany test będzie jak znalazł ;)
Pętle JavaScript - podsumowanie
To tyle na dziś jeśli chodzi o pętle w języku JavaScript. Jednocześnie zapraszam do komentowania jeśli ktoś chciałby zaproponować jeszcze jakieś usprawnienia dotyczące pętli :)