Soha ne használd a for…in-t tömbök bejárására!
Rejtélyesen viselkedik az éppen megírt JavaScript programom, és nem találom az okát. Látszólag mindent jól csináltam, és mégsem megy! Egyszer csak leesik… hogy erre nem gondoltam… de úgy látom, más sem, ezért leírom, mindannyiunk okulására.
Adott egy nagy mátrix, amiből kell csinálnom egy kisebb mátrixot úgy, hogy csak a megadott sorokat és oszlopokat veszem bele. Mi sem egyszerűbb:
originalMatrix.getParts = function( selected ) {
var newMatrix = new Matrix( selected.length, selected.length );
for( var row in selected ) {
for( var col in selected ) {
newMatrix.set( row, col, this.get( selected[row], selected[col] ) );
}
}
return newMatrix;
};
(A selected
egy egyszerű tömb, amibe push()
-sal beraktam azoknak a soroknak/oszlopoknak a sorszámát, amit szeretnék átvinni a régiből az újba.) Miért ezt a módját választottam az iterációnak? Mert elegánsabbnak tartottam, a klasszikus for ciklust:
for ( var row = 0; row < selected.length; row++ ) {
for ( var col = 0; col < selected.length; col++ ) {
newMatrix.set( row, col, this.get( selected[row], selected[col] ) );
}
}
Vagy, hogy mit ne mondjak, a .forEach()
használatát:
var thisMatrix = this;
selected.forEach( function( oldRow, newRow ) {
selected.forEach( function( oldCol, newCol ) {
newMatrix.set(newRow, newCol, thisMatrix.get( oldRow, oldCol ) );
});
});
…és mekkorát tévedtem!
A mátrix „osztály”, amit használnom kellett, egyetlen tömbben tárolja az adatokat, az elérés függvényei egyszerűek:
//indexes start at zero
Matrix.prototype.get = function (r,c) {
return this.data[r*this.cols + c];
}
Matrix.prototype.set = function (r,c,x) {
this.data[r*this.cols + c]=x;
}
És ezen a ponton történtek a bajok.
Ugyanis a for-in a megkapott objektum felsorolható tulajdonságainak nevein megy végig, nem a megkapott tömb indexein. Egy tulajdonság neve pedig, akárhogy is nézzük, egy string. És ahogyan Török Gábor kifejtette nemrég, JavaScriptben sajnos ugyanazt az operátort használhatjuk összeadásra és string összefűzésre. Így alakult, hogy a newMatrix.set
törzsében a következő kifejezés ](string)r * (number)this.cols + (string)c
. Persze, hogy a data
ezen attribútumait hiába állítgattam.
Sajnos ezt az apróságot nem szokás megemlíteni, sőt a Google-ban rákeresve az első helyeken mind tömbbel mutatják be a for-in használatát. Szomorú.
(Van még egy ok, ami miatt célszerű kerülni a használatát – de ezt néha megemlítik és könnyebb is rájönni a hiba okára, ha belefutunk –, nevezetesen ha az input tömb egyéb tulajdonságokkal is rendelkezik, akkor azok is megjelennek a felsorolásban.)
■
Rosszra kerestél
Anno ugyanebbe már én is belefutottam. Érdekessége a for-in-nek, hogy a prototípus lánctól is örököl mindent, aminek kivédésére a
hasOwnProperty()
metódus használható.Nem egészen
Az alábbi példában a tömb páros indexű elemeihez hozzáadjuk az őt követő (páratlan indexű) elemet:
Feltételezés
var x = []; x["3"] = 3
is teljesen valid.Például:
Nem arra való
A
for-in
jól működik, nincs vele semmi gondom, viszont nem tömbökre való, hanem objektumokra. Az objektumok tulajdonságainak a nevein megy végig. A tömbnek meg indexei vannak.Persze, megoldható, ezt hívják work-around-nak. De ki az, aki minden egyes
for-in
tömb bejárásnál megoldja, hogy a tulajdonság nevet tömb indexé alakítja? Szerintem senki. Mert aki tömb indexet akar, azfor
ciklust, vagyforEach
-et használ. Vagyfor-in
-t, és hosszú időt tölt a hibakereséssel, amikor nagyon nem az történik, amit szeretett volna. Végignézi a kódját, elvileg mindent mindenhol jól írt… elvileg, mert becsapta magát az i (mint index) jelöléssel, mert az nem number, hanem string: k (mint key) kellett volna, ha már egy betűset akar. Nem afor-in
hibája, hanem a programozóé. A for-in nem erre való.hasOwnProperty
_.indexOf(needed, property) === -1
.Cross-browser
parseInt esetleg?
BTW, en siman felulutom az Array.prototype.forEach-et ha nincs (MDC-n ott van az ES5-nek megfelelo feluluto kod), es vegigmegyek azzal.
Én nem szeretem a forEach-et,
Kisebb cilusokhoz még jó, de egymásba ágyazott hosszabb ciklusokhoz már gáz lehet. Én például már elég sokszor hagytam el a kontextust a végéről, hogy megunjam. Ami még zavaró, hogy a break és continue támogatása csak try-catch-el valósítható meg Visitorral.
ne is.. :)
Ha asszociatív tömböt akarsz használni, akkor használj Hash táblát.
(gyakorlatilag JSON)
biztos hogy jol ertetted,
sehol nem lattam a topicban, hogy asszociativ tombot szeretet volna letrehozni.
pont hogy normal integerrel indexeltet.
a gond az volt, hogy a for i in szerkezetben az indexek integer helyett string tipussal jottek, ami bekavart a kodban.
ha asszociativ tombot(objectet...) hasznalt volna, akkor nem jott volna elo ez a problema, de a postnak nem ez a lenyege.
Tyrael
persze.. :)
(Ami megzavart, hogy miért akar valaki for in-el végig iterálni egy normál tömbbön? Tényleg.. Miért is?)
Megszokta
Azt hittem ennél
(*tömbön - nehezen indult a reggel.. :P
Ha asszociatív tömböt akarsz
Pontosan az a probléma, hogy javascriptben a tömb is valójában hash tábla (néhány extra függvénnyel), míg más nyelvekben a rendes tömb és az asszociatív tömb külön típusok, és a for operátor mindegyikre a logikus módon viselkedik.
JS ismeret hiánya
Infinite
1/0
kifejezés végtelent ad vissza.Érthető
A soraid között is olvastam, így exkuzálnám magam, amennyiben előző hozzászólásomból bármiféle minősítés lett volna kihallható. Mások az ismereteink, így más dolgokat találunk evidensnek.
Egyetértek
A
for-in
az esetek messze túlnyomó többségében tökéletes választás lehet a tömbök esetén is (hiszen azt csak meg tudjuk jegyezni, adtunk-e hozzá új metódust vagy tulajdonságot), a gond azzal van, hogy a maradék néhány esetben viszont nagyon könnyű figyelmetlennek lenni…Én sokáig úgy voltam vele,
for-in
működése triviális, de mondjuk előkerült ahasOwnProperty
, aminek a létezéséről nem is tudtam.Csak óvatosan
Javascriptben a tömb olyan objektum, ami speciálisan kezel bizonyos property neveket:
- egy property név akkor tömb index, ha ToString(ToUint32(P)) === ToUint32(P)
- van neki length nevű property-je, aminek az értéke eggyel nagyobb, mint a legnagyobb tömb index property neve
(15.4, ECMA-262, 3rd edition, December 1999)
pl:
z=[];
z[3]=1;
alert(z.length) // 4
z[2]=2;
alert(z.length) // továbbra is 4
alert(z[1]) // undefined
alert(z.toString()) // ",,2,2"
Tehát igen, jól látja az író, a for ... in szerkezet nem való tömbök bejárására (sőt, ha 0-tól length-ig iterál a ciklus, akkor is kell ellenőrizni, hogy egy adott elem undefined-e)
Undefined
undefined
is hordoz értéket, a 0. és 1. elemünk fontos információkat hordozhat a program működésében, és léteznek a tömbben.Re:Soha ne használd a for…in-t tömbök bejárására!
A rejtélyes alatt mit értesz?
Szerintem simán be lehetne járni tömböket a for(i in tomb)-el. Akkor szokott gond lenni, ha ez nem teljesen tömb, hanem vm keretrendszer speciális eleme (prototype, jquery stb), mert ilyenkor a keretrendszer az általa használt objektumokba beletesz mindenféle saját maga által használt segédváltozót, függvényt stb. és a for .. in ezt is felolvassa.
Dani
es most olvasd vegig a cikket
Tyrael
Jíííháá, támadnak a write
itt egy kis script, ami
A másik felvetésre, aki beletesz a tömbbe néhány elemet utána az x-ediknek ad értéket és a length megnő x+1-re a magyarázat egyszerű, ott tényleg tömbről van szó és mit tömb, ha adunk értéket az x-edik elemnek, akkor a rendszer az addig lévő összes elemet kénytelen valamivel feltölteni (undefined-el).
Failed.
string, nem number