Verwenden von Objekten in For Of-Schleifen

83

Warum ist es nicht möglich, Objekte in for-Schleifen zu verwenden? Oder ist das ein Browser-Fehler? Dieser Code funktioniert in Chrome 42 nicht. Undefiniert ist keine Funktion:

test = { first: "one"}

for(var item of test) {
  console.log(item)
}
Daniel Herr
quelle
Ist Test ein Array oder Objekt?
Kick Buttowski
9
@ KickButtowski, kannst du nicht sehen? Es ist definitiv ein Objekt.
Grün
4
für (Schlüssel von Object.keys lassen (Test)) {...}
Uhrmacher

Antworten:

69

Die for..of-Schleife unterstützt nur iterierbare Objekte wie Arrays, keine Objekte.

Verwenden Sie Folgendes, um die Werte eines Objekts zu durchlaufen:

for (var key in test) {
    var item = test[key];
}
Overv
quelle
3
@DanielHerr Mit einer .iterableMitgliedsfunktion, von der der Fehler stammt, wenn Sie versuchen, sie für ein Objekt zu verwenden (das sie nicht hat). developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Overv
4
Ich meine, warum haben Objekte das nicht? Was wäre das Problem beim nativen Hinzufügen?
Daniel Herr
3
@ DanielHerr Ich habe keine Antwort darauf, du musst die Leute fragen, die die Sprache entwerfen.
Overv
6
@DanielHerr Wenn das Objekt "Basisklasse" iterierbar wäre, würde unter anderem jede Funktion / Datum / etc "Unterklasse". Eine gründlichere / genauere Diskussion Ihrer Frage finden Sie unter esdiscuss.org/topic/es6-iteration-over-object-values#content-5 .
Natevw
5
Müssen Sie mit dieser for..in-Lösung technisch noch nicht nachsehen if (test.hasOwnProperty(key)){ ... }? Oder wird das nicht benötigt?
Tennisgent
39

Sie können diese Syntax verwenden:

let myObject = {first: "one"};

for(let [key, value] of Object.entries(myObject)) {
    console.log(key, value); // "first", "one"
}

Jedoch, Object.entries hat momentan schlechte Unterstützung funktioniert nicht im IE oder iOS Safari. Du wirstwahrscheinlich Möglicherweise benötigen Sie eine Polyfüllung.

mpen
quelle
33

Wenn Sie Daten in einem Schlüsselwertspeicher speichern, verwenden Sie bitteMap die speziell für diesen Zweck vorgesehenen.

Wenn Sie jedoch ein Objekt verwenden müssen, können Sie mit ES2017 (ES8) Folgendes verwenden Object.values:

const foo = { a: 'foo', z: 'bar', m: 'baz' };
for (let value of Object.values(foo)) {
    console.log(value);
}

Wenn dies noch nicht unterstützt wird, verwenden Sie eine Polyfill: Alternative Version fürObject.values()

Und schließlich , wenn Sie eine ältere Umgebung sind zu unterstützen, die diese Syntax nicht unterstützen, werden Sie zurückgreifen zu verwenden forEachund Object.keys:

var obj = { a: 'foo', z: 'bar', m: 'baz' };
Object.keys(obj).forEach(function (prop) {
    var value = obj[prop];
    console.log(value);
});
Qantas 94 Heavy
quelle
konnte der Objektprototyp nicht erweitert werden, um dies zu unterstützen?
Sonic Soul
1
@SonicSoul: technisch ja, aber es wird im Allgemeinen nicht empfohlen, den Object-Prototyp zu erweitern, da (so ziemlich) alles davon erbt.
Qantas 94 Heavy
1
Object.entrieskann durch Polyfüllen gefüllt werden, ohne den Prototyp zu berühren.
Mpen
5
Warum Karten anstelle von Objekten verwenden?
Daniel Herr
1
Gibt es einen Vorteil bei der Verwendung dieser komplexen Beispiele gegenüber einfachen for-in?
1252748
18

Iterator, Iterable und for..of-Schleife in ECMAScript 2015 / ES6

let tempArray = [1,2,3,4,5];

for(element of tempArray) {
  console.log(element);
}

// 1
// 2
// 3
// 4
// 5

Aber wenn wir das tun

let tempObj = {a:1, b:2, c:3};

for(element of tempObj) {
   console.log(element);
}
// error

Wir erhalten eine Fehlermeldung, weil for..of loop nur für Iterables funktioniert , dh für das Objekt, das einen @@ Iterator hat , der dem Iterator-Protokoll entspricht , was bedeutet, dass es ein Objekt mit einer nächsten Methode haben muss. Die nächste Methode akzeptiert keine Argumente und sollte ein Objekt mit diesen beiden Eigenschaften zurückgeben.

done : signalisiert, dass die Sequenz beendet wurde, wenn true, und false bedeutet, dass möglicherweise mehr Werte vorhanden sind . Dies ist das aktuelle Element in der Sequenz

Um ein Objekt iterierbar zu machen, muss es funktionieren, damit ... wir können:

1 .Make ein Objekt eine Iterable durch mystisches es die Zuweisung @@ Iterator Eigenschaft durch den Symbol.iterator property.Here ist , wie:

let tempObj = {a:1, b:2, c:3};

tempObj[Symbol.iterator]= () => ({
next: function next () {
return {
    done: Object.keys(this).length === 0,
    value: Object.keys(this).shift()
     }
    }
  })

for(key in tempObj){
 console.log(key)
}
// a
// b
// c

2.Verwenden Sie Object.entries , um eine Iterable zurückzugeben :

let tempObj = {a:1, b:2, c:3};

for(let [key, value] of Object.entries(tempObj)) {
    console.log(key, value);
}
// a 1
// b 2
// c 3

3.Verwenden Sie Object.keys wie folgt:

let tempObj = {a:1, b:2, c:3};
for (let key of Object.keys(tempObj)) {
    console.log(key);
}

// a
// b
// c

Hoffe das hilft!!!!!!

Manishz90
quelle
16

Ich habe Objekte mit diesem Code iterierbar gemacht:

Object.prototype[Symbol.iterator] = function*() {
 for(let key of Object.keys(this)) {
  yield([ key, this[key] ])
} }

Verwendung:

for(let [ key, value ] of {}) { }

Alternativ:

for(let [ key, value ] of Object.entries({})) { }
Daniel Herr
quelle
47
Keine Ahnung, warum dies die akzeptierte Lösung ist. Es ist immer eine schreckliche Idee, den Prototyp zu modifizieren, es sei denn, es handelt sich um eine Polyfüllung.
user1703761
2
@ user1703761 Es ist die akzeptierte Lösung, weil es funktioniert. Bitte erklären Sie, welche Probleme dies verursachen wird, wenn es so schrecklich ist.
Daniel Herr
9
Es gibt alle möglichen Probleme, hauptsächlich Probleme mit der Vorwärtskompatibilität. Ein Beispiel dafür ist , dass Array.prototype.includes das war vorher Namen enthält aber Moo - Tools erweitert den Prototyp und die Umsetzung war nicht kompatibel, siehe bugzilla.mozilla.org/show_bug.cgi?id=1075059 auch die Prototype - Bibliothek desaster nachschlagen;)
user1703761
4
Ich glaube, dass dies keine Vorwärtskompatibilitätsprobleme haben wird, denn wenn ein Iterator zu Objekten hinzugefügt würde, würde dies ihn überschreiben, und wenn ein Iterator zu einem Objekt-Subtyp hinzugefügt würde, würde er den Subtyp-Iterator verwenden.
Daniel Herr
4
Hey Leute, den Prototyp zu modifizieren ist eine schlechte Idee !!! Schämen wir das OP dafür, dass es tatsächlich eine Antwort auf die Frage gibt!
NiCk Newman
12

Weil das Objektliteral nicht über die Eigenschaft Symbol.iterator verfügt . Um genau zu sein, können Sie nur über String , Array , Map , Set , Argumente , NodeList (nicht allgemein unterstützt) und Generator mit for ... of- Schleife iterieren .

Um mit der Objektliteral-Iteration umzugehen, haben Sie zwei Möglichkeiten.

für in

for(let key in obj){
    console.log(obj[key]); 
}

Object.keys + forEach

Object.keys(obj).forEach(function(key){
    console.log(obj[key]);
});
Lewis
quelle
3

Die Antwort lautet Nein. Es ist nicht möglich, For..Of mit Objektliteralen zu verwenden.

Ich stimme Overv zu, dass For..Of nur für iterables ist. Ich hatte genau die gleiche Frage, weil ich Objekte verwende, um Schlüssel und Werte mit for..in zu durchlaufen. Aber ich habe gerade festgestellt, dass ES6 MAPS und SETS dafür gedacht sind.

let test = new Map();
test.set('first', "one");
test.set('second', "two");

for(var item of test) {
  console.log(item); // "one" "two"
}

Damit wird das Ziel erreicht, nicht für..In (Validierung mit hasOwnProperty ) und nicht Object.keys () verwenden zu müssen.

Darüber hinaus sind Ihre Schlüssel nicht auf Zeichenfolgen beschränkt. Sie können Zahlen, Objekte oder andere Literale verwenden.

Cuadraman
quelle
2

Objektliterale verfügen nicht über integrierte Iteratoren, die für die Arbeit mit for...ofSchleifen erforderlich sind . Wenn Sie sich jedoch nicht die Mühe machen möchten, [Symbol.iterator]Ihrem Objekt ein eigenes Objekt hinzuzufügen , können Sie einfach das verwendenObject.keys() Methode verwenden. Diese Methode gibt ein ArrayObjekt zurück, in das bereits ein Iterator integriert ist, sodass Sie es mit einer for...ofSchleife wie der folgenden verwenden können:

const myObject = {
    country: "Canada",
    province: "Quebec",
    city: "Montreal"
}

for (let i of Object.keys(myObject)) {
    console.log("Key:", i, "| Value:", myObject[i]);
}

//Key: country | Value: Canada
//Key: province | Value: Quebec
//Key: city | Value: Montreal
Chunky Chunk
quelle
Die Verwendung von Schlüsseln ist jedes Mal schwieriger als das einmalige Hinzufügen eines Iterators. Außerdem ist Object.keys () ES5.
Daniel Herr
1

Es ist möglich, einen Iterator für jedes gebende Objekt zu definieren. Auf diese Weise können Sie für jedes Objekt eine andere Logik festlegen

var x = { a: 1, b: 2, c: 3 }
x[Symbol.iterator] = function* (){
    yield 1;
    yield 'foo';
    yield 'last'
}

Dann einfach direkt iterieren x

for (let i in x){
    console.log(i);
}
//1
//foo
//last

Es ist möglich, dasselbe für das Object.prototypeObjekt zu tun und einen allgemeinen Iterator für alle Objekte zu haben

Object.prototype[Symbol.iterator] = function*() {
    for(let key of Object.keys(this)) {
         yield key 
    } 
 }

dann iteriere dein Objekt so

var t = {a :'foo', b : 'bar'}
for(let i of t){
    console.log(t[i]);
}

Oder so

var it = t[Symbol.iterator](), p;
while(p = it.next().value){
    console.log(t[p])
}
Yaki Klein
quelle
1

Ich habe nur Folgendes getan, um meine Sachen einfach zu trösten.

for (let key in obj) {
  if(obj.hasOwnProperty(key){
    console.log(`${key}: ${obj[key]}`);
  }
}
DaFrenzy
quelle
1

Wie wäre es mit Object.keys , um ein Array von Schlüsseln zu erhalten? Und dann für jeden auf dem Array ?

obj = { a: 1, b:2}
Object.keys(obj).forEach( key => console.log(`${key} => ${obj[key]}`))
Justingordon
quelle
0

Was ist mit

function* entries(obj) {
    for (let key of Object.keys(obj)) {
        yield [key, obj[key]];
    }
}

for ([key, value] of entries({a: "1", b: "2"})) {
    console.log(key + " " + value);
}
user1703761
quelle
0

in ES6 könnten Sie mit Generator gehen:

var obj = {1: 'a', 2: 'b'};

function* entries(obj) {
  for (let key of Object.keys(obj)) {
    yield [key, obj[key]];
  }
}

let generator = entries(obj);

let step1 = generator.next();
let step2 = generator.next();
let step3 = generator.next();

console.log(JSON.stringify(step1)); // {"value":["1","a"],"done":false}
console.log(JSON.stringify(step2)); // {"value":["2","b"],"done":false}
console.log(JSON.stringify(step3)); // {"done":true}

Hier ist die jsfiddle. In der Ausgabe erhalten Sie ein Objekt mit den Tasten "value"und "done". "Value"enthält alles, was Sie möchten , und "done"ist der aktuelle Status der Iteration in bool.

Serge Nikolaev
quelle
0

Mit Array Destruction können Sie es wie folgt iterieren forEach

const obj = { a: 5, b: 7, c: 9 };

Object.entries(obj).forEach(([key, value]) => {
  console.log(`${key} ${value}`); // "a 5", "b 7", "c 9"
});
Nur Rony
quelle