@Coldpeer:
@Zi00mal:
Uściślijmy przechodzenie po obiektach/tablicach w JavaScripcie. Funkcji .forEach()
i pętli for..in
nie wolno -- a czasem się wręcz nie da -- stosować zamiennie.
Tablice to specjalne obiekty, które przechowują swoje elementy pod kluczami będącymi kolejnymi indeksami od 0 do tablica.length - 1
. Np. tablica złożona z trzech stringów i jednej liczby: ["ala", "ma", "kota", 123]
. Ogólnie, w JavaScripcie, tablice mogą mieć też klucze inne niż indeksy, ale nie w JSON-ie.
Obiekty można nazwać od biedy czymś w rodzaju hashmap lub tablic asocjacyjnych z PHP. Obiekty mapują klucze (będące ciągami znaków) na wartości, np. { response: "Jakiś tekst", error: 123 }
(JSON wymaga, by klucze zawsze były otoczone cudzysłowami, ale w JS-ie zwykle można je pominąć).
I teraz: tablice mają metodę .forEach()
, o której wspomniał @Coldpeer . To poprawna metoda iteracji po tablicy, choć jest niestety wolniejsza od klasycznej pętli for:
for (var i = 0; i < tablica.length; i++) {
...
}
Szybkość iteracji jednak rzadko ma znaczenie.
Problem w tym, że obiekty nie mają metody .forEach()
-- jest ona obecna tylko na tablicach. Więc w przypadku tablic asocjacyjnych z PHP, które są zapewne serializowane jako obiekty JSON, mielibyśmy problem. Przeglądarka powiedziałaby, że data.response.forEach
to nie funkcja (tylko undefined
).
Obiekty możemy przechodzić pętlą for..in
, przy czym zmiennej sterującej nie nazwałbym elem
, bo sugeruje to element (wartość elementu), a zmienna sterująca w pętli for
to klucz. Nazwałbym ją więc raczej key
czy propertyName
(skrótowo: prop
).
Używając pętli for..in
, praktycznie zawsze powinniśmy się zabezpieczyć przed własnościami odziedziczonymi z prototypu Object.prototype
. Wszystkie obiekty w JavaScripcie dziedziczą kilka własności standardowych z Object.prototype
. Normalnie nie stanowi to problemu, bo własności standardowe mają flagę [[Enumerable]]
ustawioną na false
, a to oznacza, że nie pokazują się w pętli for..in
. Ale jeśli ktoś zrobi nieostrożnie np. tak:
Object.prototype.cośtam = function() {
// ...
};
to własność cośtam
będzie miała [[Enumerable]]
ustawione na true
i pojawi się w pętli for..in takiej jak ta, którą napisałeś. Bo Twój obiekt data.response
odziedziczy własność cośtam
po prototypie obiektu, mimo że tego nie chciałeś.
Dlatego w każdej iteracji pętli warto sprawdzić, czy dana właściwość jest tzw. "właściwością własną" (po angielsku lepiej brzmi: own property) danego obiektu (w przeciwnym wypadku wzięła się stąd, że została odziedziczona).
Zabezpieczona pętla wygląda tak:
var response = data.response; // żeby się nie powtarzać
for (var key in response) {
if (response.hasOwnProperty(key)) {
alert(key + " - " + response[key]);
}
}
Ciekawskim powiem, że i to nie jest stuprocentowe zabezpieczenie, bo wykrzaczy się, jeśli jakiś dowcipniś wstawi nam do response
właściwość o kluczu... hasOwnProperty
:-). Po stronie klienta nie musimy się tym przejmować, ale dla wielu serwerów z NodeJS-a był to problem, który trzeba było sprytnie obchodzić.