Vraag Waarom is het gebruik van "voor ... in" met array-iteratie een slecht idee?


Er is mij verteld dat ik het niet moet gebruiken for...in met arrays in JavaScript. Waarom niet?


1539
2018-02-01 09:46


oorsprong


antwoorden:


De reden is dat één constructie:

var a = []; // Create a new empty array.
a[5] = 5;   // Perfectly legal JavaScript that resizes the array.

for (var i = 0; i < a.length; i++) {
    // Iterate over numeric indexes from 0 to 5, as everyone expects.
    console.log(a[i]);
}

/* Will display:
   undefined
   undefined
   undefined
   undefined
   undefined
   5
*/

kan soms heel anders zijn dan de ander:

var a = [];
a[5] = 5;
for (var x in a) {
    // Shows only the explicitly set index of "5", and ignores 0-4
    console.log(x);
}

/* Will display:
   5
*/

Overweeg dat ook JavaScript bibliotheken kunnen dit soort dingen doen, die van invloed zijn op elke array die u maakt:

// Somewhere deep in your JavaScript library...
Array.prototype.foo = 1;

// Now you have no idea what the below code will do.
var a = [1, 2, 3, 4, 5];
for (var x in a){
    // Now foo is a part of EVERY array and 
    // will show up here as a value of 'x'.
    console.log(x);
}

/* Will display:
   0
   1
   2
   3
   4
   foo
*/


1341
2018-02-01 10:08



De for-in verklaring op zichzelf is geen "slechte praktijk", hoe het ook kan zijn verkeerd gebruiktebijvoorbeeld voor herhalen over arrays of array-achtige objecten.

Het doel van de for-in verklaring is om opsommen over objecteigenschappen. Deze uitspraak zal omhoog gaan in de prototypeketen, ook opsommen geërfd eigenschappen, iets dat soms is niet gewenst.

Ook wordt de volgorde van iteratie niet gegarandeerd door de spec. Dit betekent dat als u een array-object wilt "herhalen", u met deze instructie niet zeker weet of de eigenschappen (array-indexen) in de numerieke volgorde worden bezocht.

In JScript (IE <= 8) wordt de volgorde van opsommingen zelfs bij matrixobjecten gedefinieerd als de eigenschappen die zijn gemaakt:

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
}

Ook, sprekend over geërfde eigenschappen, als je, bijvoorbeeld, de Array.prototype object (zoals sommige bibliotheken zoals MooTools dat doen), worden die eigenschappen ook opgesomd:

Array.prototype.last = function () { return this[this.length-1]; };

for (var p in []) { // an empty array
  // last will be enumerated
}

Zoals ik eerder al zei herhalen over arrays of array-achtige objecten, het beste is om een sequentiële lus, zoals een gewoon oud for/while lus.

Wanneer u alleen de. Wilt opsommen eigen eigenschappen van een object (degenen die niet zijn geërfd), kunt u de hasOwnProperty methode:

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

En sommige mensen raden zelfs aan om de methode rechtstreeks vanuit te bellen Object.prototype om problemen te voorkomen als iemand een eigenschap met de naam toevoegt hasOwnProperty naar ons doel:

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

356
2017-11-23 21:22



Er zijn drie redenen waarom u dit niet zou moeten gebruiken for..in om matrixelementen te herhalen:

  • for..in zal lus over alle eigen en overgenomen eigenschappen van het array-object die dat niet zijn DontEnum; dat betekent dat iemand eigenschappen toevoegt aan het specifieke array-object (er zijn geldige redenen hiervoor - ik heb het zelf gedaan) of veranderd Array.prototype (wat wordt beschouwd als slechte praktijk in code die goed zou moeten werken met andere scripts), deze eigenschappen zullen ook opnieuw worden herhaald; overgeërfde eigenschappen kunnen worden uitgesloten door te controleren hasOwnProperty(), maar dat zal je niet helpen met eigenschappen die zijn ingesteld in het array-object zelf

  • for..in is niet gegarandeerd om het bestellen van elementen te behouden

  • het is traag omdat je alle eigenschappen van het array-object en zijn hele prototypeketen moet lopen en toch alleen de naam van het object krijgt, dus om de waarde te krijgen, is een extra lookup vereist


101
2018-02-01 14:04



Omdat voor ... opsommen door het object dat de array bevat, niet de array zelf. Als ik een functie toevoeg aan de prototypeketen van de array, wordt die ook opgenomen. D.w.z.

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

Dit zal schrijven:

0 = foo
1 = bar
myOwnFunction = function () {alert (this); }

En aangezien je nooit zeker weet dat er niets aan de prototypeketen wordt toegevoegd, gebruik je gewoon een lus om de array op te sommen:

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

Dit zal schrijven:

0 = foo
1 = bar

48
2018-02-01 10:08



Op zichzelf is er niets mis met het gebruik van for-in op arrays. For-in itereert over de eigenschapnamen van een object en in het geval van een "out-of-the-box" -array komen de eigenschappen overeen met de array-indexen. (De ingebouwde propertes houden van length, toString enzovoort worden niet opgenomen in de iteratie.)

Als uw code (of het framework dat u gebruikt) echter aangepaste eigenschappen aan arrays of het array-prototype toevoegt, worden deze eigenschappen opgenomen in de iteratie, wat waarschijnlijk niet is wat u wilt.

Sommige JS-raamwerken, zoals Prototype, modificeren het prototype van de array. Andere frameworks zoals JQuery doen dat niet, dus met JQuery kun je veilig for-in gebruiken.

Als u twijfelt, moet u waarschijnlijk niet voor-in gebruiken.

Een alternatieve manier om door een array te itereren, is een for-loop gebruiken:

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

Dit heeft echter een ander probleem. Het probleem is dat een JavaScript-array "gaten" kan hebben. Als je definieert arr als:

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

De array heeft dan twee items, maar een lengte van 101. Het gebruik van de for-in levert twee indexen op, terwijl de for-loop 101 indexen oplevert, waarbij de 99 een waarde heeft van undefined.


37
2018-02-01 10:34



Naast de redenen die in andere antwoorden worden gegeven, wilt u misschien niet de structuur "voor ... in" gebruiken als u wiskundige bewerkingen met de tellervariabele moet doen omdat de lus door de namen van de eigenschappen van het object en dus de variabele wordt herhaald. is een string.

Bijvoorbeeld,

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

zal schrijven

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

terwijl,

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

zal schrijven

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

Dit kan natuurlijk gemakkelijk worden opgelost door het op te nemen

ii = parseInt(ii);

in de lus, maar de eerste structuur is directer.


28
2017-09-02 02:29



Vanaf 2016 (ES6) kunnen we gebruiken for…of voor array-iteratie, zoals John Slegers al heeft opgemerkt.

Ik zou deze eenvoudige demonstratiecode graag willen toevoegen, om dingen duidelijker te maken:

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++;
    }

De console toont:

for...of:

0: undefined
1: undefined
2: undefined
3: undefined
4: undefined
5: xyz

for...in:

0: 5
1: foo

Met andere woorden:

  • for...of telt van 0 tot 5, en negeert ook Array.prototype.foo. Het toont array waarden.

  • for...in geeft alleen de 5, negeren van niet gedefinieerde array-indices, maar toevoegen foo. Het toont array eigendomsnamen.


24
2018-03-10 04:29