Kodėl naudojant „už ...“ su masyvo iteracija yra bloga idėja?

Man buvo pasakyta, kad nenaudojau for...in su masyvais javascript. Kodėl gi ne?

1579
01 февр. lYriCAlsSH rinkinys 01 vasaris 2009-02-01 12:46 '09, 12:46, 2009-02-01 12:46
@ 25 atsakymai

Priežastis yra ta, kad viena konstrukcija:

Pats veikimas savaime nėra bloga praktika, tačiau gali būti piktnaudžiaujama, pvz., Kartoti ant masyvo ar masyvo tipo objektų.

For for-in operatoriaus tikslas yra išvardyti objekto savybes. Šis operatorius augs prototipų grandinėje, taip pat nurodydamas paveldėtas savybes, kurios kartais nėra pageidautinos.

Be to, specifikacijoje neužtikrinama iteracijų tvarka. Tai reiškia, kad jei norite "iteruoti" masyvo objektą, šiuo teiginiu negalite būti tikri, kad ypatybės (masyvo indeksai) bus aplankytos skaitine tvarka.

Pavyzdžiui, „JScript“ (IE <= 8), skaičiavimo tvarka, net ir „Array“ objektuose, apibrėžiama kaip savybės sukurtos:

 var array = []; array[2] = 'c'; array[1] = 'b'; array[0] = 'a'; for (var p in array) { //... p will be "2", "1" and "0" on IE } 

Be to, kalbant apie paveldėtas savybes, jei, pavyzdžiui, Array.prototype objektą (pvz., Kai kurios bibliotekos, pvz., „MooTools“), šios savybės taip pat bus išvardytos:

border=0
 Array.prototype.last = function () { return this[this.length-1]; }; for (var p in []) { // an empty array // last will be enumerated } 

Kaip jau minėjau, iteruojant ant masyvų ar objektų, pvz., Masyvo, geriausia naudoti nuoseklią kilpą, pavyzdžiui, paprastą paleidimą.

Jei norite išvardyti tik objekto savybes (tas, kurios nėra paveldėtos), galite naudoti hasOwnProperty metodą:

 for (var prop in obj) { if (obj.hasOwnProperty(prop)) { // prop is not inherited } } 

Kai kurie žmonės netgi rekomenduoja skambinti metodu tiesiogiai iš Object.prototype kad išvengtumėte problemų, jei kažkas prideda objektą, pavadintą hasOwnProperty

 for (var prop in obj) { if (Object.prototype.hasOwnProperty.call(obj, prop)) { // prop is not inherited } } 
359
24 нояб. atsakymą pateikė CMS 24 nov. 2010-11-24 00:22 '10 - 0:22 2010-11-24 00:22

Yra trys priežastys, dėl kurių neturėtumėte naudoti, for..in norite kartoti per masyvo elementus:

  • for..in bus padengtos visos savo ir paveldėtos masyvo objekto savybės, kurios nėra DontEnum ; tai reiškia, kad jei kas nors prideda savybes konkrečiam masyvo objektui (tam yra gerų priežasčių - aš tai padariau patys) arba pasikeitė „ Array.prototype (kuris laikomas bloga kodo praktika, kuri turėtų gerai veikti su kitais scenarijais), tai ypatybės taip pat bus pakartotos; paveldėtos savybės gali būti pašalintos tikrinant hasOwnProperty() , bet tai nepadeda jums nustatyti pačių masyvo objekto nustatytų savybių

  • for..in nėra garantuotas elementų užsakymo išsaugojimas

  • jis yra lėtas, nes jums reikia eiti per visas masyvo objekto savybes ir visą jos prototipų grandinę ir vis tiek gauti tik nuosavybės pavadinimą, t. gauti vertę, reikia papildomos paieškos

101
01 февр. Christof Feb 01 atsakymas 2009-02-01 17:04 '09 at 17:04 2009-02-01 17:04

Nes ... paskaitoje per objektą, kuriame yra masyvas, o ne pats masyvas. Jei pridedu funkciją prie masyvo prototipo grandinės, ji taip pat bus įtraukta. Tai yra.

 Array.prototype.myOwnFunction = function() { alert(this); } a = new Array(); a[0] = 'foo'; a[1] = 'bar'; for(x in a){ document.write(x + ' = ' + a[x]); } 

Jis parašys:

 0 = foo 1 = baras myOwnFunction = funkcija () {įspėjimas (tai);  }

Ir kadangi niekada negalite būti tikri, kad nieko nebus pridėta prie prototipo grandinės, tiesiog naudokite „loop“, kad išvardintumėte masyvą:

 for(i=0,x=a.length;i<x;i++){ document.write(i + ' = ' + a[i]); } 

Jis parašys:

 0 = foo 1 = baras
49
01 февр. Atsakymas duotas Pim Jager 01 vasario mėn. 2009-02-01 13:08 '09, 13:08 2009-02-01 13:08

Nėra nieko gausaus, kai naudojate masyvus. For-in kartojasi per objekto nuosavybės vardus, o išorinio bloko masyvo atveju savybės atitinka masyvo indeksus. (Įmontuoti atributai, tokie kaip length , toString ir tt Neįtraukti į iteraciją.)

Tačiau, jei jūsų kodas (arba naudojama struktūra) masyvams arba masyvo prototipui prideda pasirinktines savybes, tada šios savybės bus įtrauktos į iteraciją, kuri tikriausiai nėra tai, ko norite.

Kai kurios JS struktūros, pvz., Prototipas, modifikuoja „Array“ prototipą. Kitos struktūros, pvz., JQuery, neveikia, todėl galite saugiai naudotis „in-in“ su „jQuery“.

Jei kyla abejonių, tikriausiai neturėtumėte naudoti in-in.

Alternatyvus būdas kartoti per masyvą yra naudoti kilpą:

 for (var ix=0;ix<arr.length;ix++) alert(ix); 

Tačiau tai dar viena problema. Problema ta, kad „javascript“ masyvas gali turėti „skyles“. Jei nustatote arr kaip:

 var arr = ["hello"]; arr[100] = "goodbye"; 

Tada masyvas turi du elementus, tačiau ilgis yra 101. Naudojant for-in, du indeksai bus rodomi, o for-loop suteiks 101 indeksą, kur 99 yra undefined .

37
01 февр. atsakymą pateikė JacquesB 01 vasaris. 2009-02-01 13:34 '09 13:34 2009-02-01 13:34

Be kitų atsakymų pateiktų priežasčių, galite nenorėti naudoti „for ... in“ struktūros, jei reikia atlikti matematiką su skaitiklių kintamuoju, nes kilpa kartojasi per objekto nuosavybės pavadinimus ir todėl kintamasis yra eilutė.

Pavyzdžiui

 for (var i=0; i<a.length; i++) { document.write(i + ', ' + typeof i + ', ' + i+1); } 

rašys

 0, number, 1 1, number, 2 ... 

tada

 for (var ii in a) { document.write(i + ', ' + typeof i + ', ' + i+1); } 

rašys

 0, string, 01 1, string, 11 ... 

Žinoma, tai lengva įveikti sukant

 ii = parseInt(ii); 

kilpa, bet pirmoji struktūra yra tiesioginė.

29
02 сент. atsakymas pateikiamas ctmiddle 02 Sep. 2009-09-02 05:29 '09 at 5:29 am 2009-09-02 05:29

Nuo 2016 m. (ES6) mes galime naudoti for…of kad kartotume masyvą, kaip jau pažymėjo Johnas Slegersas.

Aš tik norėjau pridėti šį paprastą demo kodą, kad jis būtų aiškesnis:

 Array.prototype.foo = 1; var arr = []; arr[5] = "xyz"; console.log("for...of:"); var count = 0; for (var item of arr) { console.log(count + ":", item); count++; } console.log("for...in:"); count = 0; for (var item in arr) { console.log(count + ":", item); count++; } 

Konsolėje rodoma:

 for...of: 0: undefined 1: undefined 2: undefined 3: undefined 4: undefined 5: xyz for...in: 0: 5 1: foo 

Kitaip tariant:

  • for...of apskaičiuojamas nuo 0 iki 5, taip pat ignoruojamas Array.prototype.foo . Jis rodo masyvo reikšmes. .

  • for...in tik 5 sąrašuose, ignoruojant neapibrėžtus matricų indeksus, bet pridedant foo . Jis rodo masyvo savybių pavadinimus. .

26
10 марта '16 в 7:29 2016-03-10 07:29 atsakymas pateikiamas MarcG kovo 10 d. 16 val. 07:29 2016-03-10 07:29

Be to, kad ... in sąrašą išvardijamos visos skaičiuojamos savybės (tai nėra tas pats, kas „visi masyvo elementai“!), Žr. Http://www.ecma-international.org/publications/files/ECMA-ST /Ecma-262.pdf , 12.6.4 skirsnis (penktasis leidimas) arba 13.7.5.15 (7-asis leidimas):

Nenurodyta ypatybių sąrašo mechanika ir tvarka ...

(Pabraukti mano.)

Tai reiškia, kad jei to norėtų naršyklė, ji gali eiti per savybes tokia tvarka, kuria jie buvo įterpti. Arba skaitine tvarka. Arba leksinėje eilutėje (kur „30“ ateina į „4“! Prisiminkite visus objekto raktus - ir todėl visi masyvo indeksai - iš tiesų yra stygos, todėl tai yra prasminga). Jis gali pereiti per kibirą, jei jis įgyvendina objektus kaip maišos lenteles. Arba atlikite bet kurį iš šių ir pridėkite „atgal“. Naršyklė netgi gali kartoti atsitiktinai ir būti suderinama su ECMA-262, jei ji kiekvieną kartą tiksliai aplankė kiekvieną turtą.

Praktiškai dauguma naršyklių šiuo metu mėgsta kartoti tą pačią tvarką. Bet nieko nereiškia, ko jums reikia. Šis įgyvendinimas yra konkretus ir gali bet kuriuo metu pasikeisti, jei kitas būdas yra efektyvesnis.

Bet kokiu atveju, for ... neužima jokios pavedimo reikšmės. Jei rūpi užsakymas, būkite atviri ir naudokite įprastą kilpą su indeksu.

21
14 мая '12 в 19:26 2012-05-14 19:26 atsakymą pateikė cHao gegužės 14 d. 12 val. 19:26 2012-05-14 19:26

Trumpas atsakymas: tai tiesiog ne verta.


Ilgesnis atsakymas: tai tiesiog nėra verta, net jei nereikalaujama iš eilės einančių elementų ir optimalaus veikimo.


Ilgas atsakymas: tai tiesiog nėra verta, dėl šių priežasčių:

  • Naudojant for (var i in array) {} , „masyvas“ bus interpretuojamas kaip bet kuris kitas švarus objektas, praeinantis objekto nuosavybės grandinę ir galiausiai atliekantis lėčiau nei indekso pagrindu.
  • Negalima garantuoti objekto savybių grąžinimo iš eilės, kaip tikėtasi.
  • Naudojant hasOwnProperty() arba isNaN() patikrinimus, norint filtruoti objekto savybes, yra papildoma pridėtinė vertė, kuri verčia jį atlikti (dar daugiau) lėčiau. Be to, įvedus tokią papildomą logiką, pagrindinė priežastis, kodėl ji naudojama, yra, nes dėl labiau suspausto formato.

Dėl šių priežasčių net nepavyksta pasiekti priimtino kompromiso tarp našumo ir patogumo. Iš tiesų nėra jokios naudos, jei ketinama apdoroti masyvą kaip gryną objektą ir atlikti operacijas masyvo objekto savybėmis.

20
14 марта '13 в 10:12 2013-03-14 10:12 atsakymą pateikė „ WynandB “ kovo 13 d. 13 val. 10:12 2013-03-14 10:12

Iš esmės dvi priežastys:

Vienas

Kaip ir kiti, galite gauti raktus, kurie nėra jūsų masyve arba paveldėti iš prototipo. Taigi, jei, pvz., Biblioteka prideda nuosavybę į Array arba Object prototipus:

 Array.prototype.someProperty = true 

Jūs gausite ją kaip kiekvieno masyvo dalį:

 for(var item in [1,2,3]){ console.log(item) // will log 1,2,3 but also "someProperty" } 

Tai galite išspręsti naudodami hasOwnProperty metodas:

 var ary = [1,2,3]; for(var item in ary){ if(ary.hasOwnProperty(item)){ console.log(item) // will log only 1,2,3 } } 

bet tai pasakytina apie iteraciją per bet kurį objektą, turintį for-in kilpą.

Du

Paprastai elementų elementų masyve tvarka yra svarbi, bet for-in kilpa nebūtinai kartos teisinga tvarka, nes ji traktuoja masyvą kaip objektą, įgyvendinamą JS, o ne kaip masyvą. Tai atrodo kaip nedidelis dalykas, bet jis gali iš tikrųjų išprasti programas ir yra sunku derinti.

15
04 февр. Atsakyti Lior 04 Feb. 2014-02-04 19:54 '14, 19:54, 2014-02-04 19:54

Nes jis nurodo objekto laukus, o ne rodiklius. Jūs galite gauti vertę su indeksu „ilgis“, ir aš abejoju, ar norite.

15
01 февр. atsakymas duotas vava 01 Feb. 2009-02-01 12:50 '09, 12:50 2009-02-01 12:50

Problema susijusi su for ... in ... - ir tai tampa problema, kai programuotojas tikrai nesupranta kalbos; tai nėra klaida ar kažkas kita; tai, kad ji kartojasi per visus objekto elementus (gerai, visi išvardyti nariai, tačiau ši dalis šiuo metu yra). Jei norite kartoti tik indeksuotas indekso savybes, vienintelis garantuotas būdas saugoti semantiškai nuoseklias reikšmes yra naudoti sveikojo skaičiaus indeksą (t.y., stiliaus for (var i = 0; i < array.length; ++i) ).

Bet kuriam objektui gali būti būdingos savybės. Nebūtų nieko baisiausio įkeliant papildomų savybių į masyvo pavyzdį. Kodas, kuris nori matyti tik indeksuotas savybes, pvz., Masyvą, todėl turi būti laikomasi sveikojo skaičiaus indeksu. Kodą, kuris visiškai žino, for ... in tikrai ir tikrai turėtų matyti visas savybes, gerai, tai taip pat yra normalu.

14
24 нояб. atsakymas pateiktas Pointy 24 lapkričio. 2010-11-24 00:23 '10 - 0:23 2010-11-24 00:23

Nemanau, kad turiu ką nors pridėti. Atsakymas į triptichą ar CMS atsakymą, kodėl kai kuriais atvejais turėtumėte vengti naudoti for-in .

Vis dėlto norėčiau pridurti, kad šiuolaikinėse naršyklėse yra alternatyvi for-in , kurią galima naudoti tais atvejais, kai negalima naudoti in for-in . Ši alternatyva yra:

 for (var item of items) { console.log(item); } 

Pastaba:

Deja, nė viena „Internet Explorer“ versija nepalaiko šios funkcijos („ Edge 12+“ ), todėl jūs turite šiek tiek ilgiau laukti, kol galėsite jį naudoti savo gamybos kode kliento pusėje. Tačiau serverio pusėje turi būti saugu naudoti JS kodą (jei naudojate „ Node.js“ ).

10
21 февр. John Slegers atsakymas 21 vasaris 2016-02-21 23:48 '16 at 23:48 PM 2016-02-21 23:48

Be to, dėl semantikos, for, in kelio procesų matricose (ty kaip ir bet kuris kitas „JavaScript“ objektas) nėra suderintas su kitomis populiariomis kalbomis.

 // C# char[] a = new char[] {'A', 'B', 'C'}; foreach (char x in a) System.Console.Write(x); //Output: "ABC" // Java char[] a = {'A', 'B', 'C'}; for (char x : a) System.out.print(x); //Output: "ABC" // PHP $a = array('A', 'B', 'C'); foreach ($a as $x) echo $x; //Output: "ABC" // JavaScript var a = ['A', 'B', 'C']; for (var x in a) document.write(x); //Output: "012" 
8
25 окт. atsakymas pateikiamas matpop 25 oct. 2013-10-25 19:43 '13, 19:43, 2013-10-25 19:43

TL DR: Naudojant „ for in loop“ matricose nėra blogis, iš tikrųjų yra tiesa.

Manau, kad kilpa yra JS perlas, jei jis tinkamai naudojamas masyvuose. Turite turėti visišką jūsų programinės įrangos kontrolę ir žinoti, ką darote. Pažvelkite į minėtus trūkumus ir paneigkite juos po vieną.

  • Ji taip pat pereina per paveldėtas savybes . Visų pirma, bet kokie Array.prototype turi būti įvykdyti naudojant Object.defineProperty() , ir jų enumerable deskriptorius turi būti nustatytas kaip false . Bet kuri biblioteka, kuri to nedaro, neturėtų būti naudojama.
  • Po to skaičiuojamos ypatybės, kurias pridėsite prie paveldėjimo grandinės: kai paleidžiate masyvo poklasį Object.setPrototypeOf arba extend klasėje. Dar kartą turite naudoti Object.defineProperty() , kuris pagal nutylėjimą nustato Object.defineProperty() , enumerable ir configurable nuosavybės deskriptorius. Pamatysime masyvo poklasio pavyzdį ...

7
28 янв. atsakymas Redu 28 d 2017-01-28 11:16 '17 at 11:16 2017-01-28 11:16

Svarbus aspektas yra tai, for...in įterpti tik po objekto esančiomis savybėmis, kurių skaičiuojamos nuosavybės atributas nustatytas tiesu . Todėl, jei bandote pasikartoti objektą, naudodami for...in , tada bet kurios savybės gali būti praleistos, jei jų apskaitytinas nuosavybės atributas yra klaidingas. Galima keisti įprastus „Array“ objektų skaičiuojamo turto atributą, kad tam tikri elementai nebūtų įtraukti. Nors, kaip taisyklė, nuosavybės požymiai linkę nurodyti objekto funkcijos savybes.

Galite patikrinti turto, kurį galima nuskaityti, atributo vertę:

 myobject.propertyIsEnumerable('myproperty') 

Arba gaukite visus keturis nuosavybės požymius:

 Object.getOwnPropertyDescriptor(myobject,'myproperty') 

Tai funkcija, kurią galima gauti ECMAScript 5 - ankstesnėse versijose neįmanoma pakeisti skaičiuojamo turto atributo vertės (visada buvo teisinga).

7
10 мая '13 в 21:20 2013-05-10 21:20 atsakymą davė Pierz , gegužės 10 d. 13 val. 21:20 2013-05-10 21:20

Be kitų problemų, „for..in“ sintaksė tikriausiai yra lėtesnė, nes indeksas yra eilutė, o ne sveikasis skaičius.

 var a = ["a"] for (var i in a) alert(typeof i) // 'string' for (var i = 0; i < a.length; i++) alert(typeof i) // 'number' 
7
06 июня '12 в 1:52 2012-06-06 01:52 atsakymas dc1 birželio 06 d. 12 val. 1:52 2012-06-06 01:52

for / in darbą su dviejų tipų kintamaisiais: hashtables (asociatyviosios matricos) ir masyvu (nonassociative).

„JavaScript“ automatiškai nustatys, kaip pereiti per elementus. Todėl, jei žinote, kad jūsų masyvas yra tikrai ne asociatyvus, galite naudoti for (var i=0; i<=arrayLen; i++) ir praleisti automatinio aptikimo iteraciją.

Bet, mano nuomone, geriau naudoti / in , procesas, kurio reikia šiam automatiniam aptikimui, yra labai mažas.

Tikrasis atsakymas tai priklauso nuo to, kaip naršyklė analizuoja / interpretuoja „JavaScript“ kodą. Tai gali skirtis skirtingose ​​naršyklėse.

Negaliu galvoti apie kitus tikslus: nenaudokite / in ;

 //Non-associative var arr = ['a', 'b', 'c']; for (var i in arr) alert(arr[i]); //Associative var arr = { item1 : 'a', item2 : 'b', item3 : 'c' }; for (var i in arr) alert(arr[i]); 
7
24 нояб. Atsakymą pateikė Ricardo lapkričio 24 d. 2010-11-24 00:44 '10 - 0:44 2010-11-24 00:44

Jei jis bus kartojamas per prototipo grandinės objektams priklausančias savybes, jei nesate atsargūs.

Jūs galite naudoti for.. in , tiesiog patikrinkite kiekvieną turtą su hasOwnProperty .

6
24 нояб. Atsakymas pateiktas JAL lapkričio 24 d. 2010-11-24 00:20 '10 - 0:20 2010-11-24 00:20

Turėtumėte naudoti for(var x in y) tik nuosavybės sąrašuose, o ne objektuose (kaip aprašyta aukščiau).

5
24 нояб. atsakymas pateiktas vartotojo268396 lapkritis 24 2010-11-24 00:25 '10 - 0:25 2010-11-24 00:25

Tai nebūtinai yra bloga (remiantis tuo, ką darote), bet masyvų atveju, jei kažkas yra įtraukta į „ Array.prototype , tada gausite keistų rezultatų. Kur tikitės, kad šis ciklas truks tris kartus:

 var arr = ['a','b','c']; for (var key in arr) { ... } 

Jei funkcija helpfulUtilityMethod pridedama prie „ Array prototype , tuomet jūsų ciklas veiks keturis kartus: key bus 0 , 1 , 2 ir helpfulUtilityMethod . Jei tikėjotės tik sveikų skaičių, oi.

5
24 нояб. atsakymas duotas josh3736 24 nov. 2010-11-24 00:25 '10 - 0:25 2010-11-24 00:25

Naudojant „ for...in loop for...in masyvui, neteisinga, nors galiu spėti, kodėl kažkas jums pasakė, kad:

1.) Jau yra aukštesnės eilės funkcija arba metodas, kuris turi šį tikslą masyvui, tačiau turi daugiau funkcionalumo ir kompaktiškesnės sintaksės, vadinamos „forEach“: Array.prototype.forEach(function(element, index, array) {} );

2.) Array visada turi ilgį, bet for...in ir forEach neveikia jokios 'undefined' vertės, tik indeksams, turintiems tam tikrą reikšmę. Todėl, jei priskiriate tik vieną vertę, šie ciklai bus atlikti tik vieną kartą, tačiau, kadangi masyvas yra įtrauktas į sąrašą, jis visada bus ilgiausias iki didžiausio indekso, turinčio tam tikrą reikšmę, tačiau šis ilgis gali būti nepastebėtas naudojant šias kilpas.