Ich werde es versuchen. Beachten Sie, dass ich nicht mit ECMA verbunden bin und keinen Einblick in deren Entscheidungsprozess habe. Daher kann ich nicht definitiv sagen, warum sie etwas getan haben oder nicht. Ich werde jedoch meine Annahmen darlegen und mein Bestes geben.
1. Warum überhaupt ein for...of
Konstrukt hinzufügen ?
JavaScript enthält bereits ein for...in
Konstrukt, mit dem die Eigenschaften eines Objekts iteriert werden können. Es ist jedoch nicht wirklich eine forEach-Schleife , da sie alle Eigenschaften eines Objekts auflistet und nur in einfachen Fällen vorhersehbar funktioniert.
Es bricht in komplexeren Fällen zusammen (auch bei Arrays, bei denen seine Verwendung durch die Sicherheitsvorkehrungen, die für die korrekte Verwendung mit einem Array erforderlich sind, entweder entmutigt oder gründlich verschleiert wird ). Sie können das umgehen, indem Sie (unter anderem) verwenden, aber das ist etwas klobig und unelegant. for...in
hasOwnProperty
Daher gehe ich davon aus, dass das for...of
Konstrukt hinzugefügt wird, um die mit dem for...in
Konstrukt verbundenen Mängel zu beheben und eine größere Nützlichkeit und Flexibilität beim Iterieren von Dingen bereitzustellen. Menschen neigen dazu, for...in
als forEach
Schleife zu behandeln , die im Allgemeinen auf jede Sammlung angewendet werden kann und in jedem möglichen Kontext vernünftige Ergebnisse liefert, aber das passiert nicht. Die for...of
Schleife behebt das.
Ich gehe auch davon aus, dass es wichtig ist, dass vorhandener ES5-Code unter ES6 ausgeführt wird und dasselbe Ergebnis wie unter ES5 liefert, sodass beispielsweise am Verhalten des for...in
Konstrukts keine wesentlichen Änderungen vorgenommen werden können .
2. Wie funktioniert das for...of
?
Die Referenzdokumentation ist für diesen Teil hilfreich. Insbesondere wird ein Objekt berücksichtigt, iterable
wenn es die Symbol.iterator
Eigenschaft definiert .
Die Eigenschaftsdefinition sollte eine Funktion sein, die die Elemente in der Auflistung einzeln zurückgibt und ein Flag setzt, das angibt, ob weitere Elemente abgerufen werden sollen oder nicht. Für einige Objekttypen werden vordefinierte Implementierungen bereitgestellt , und es ist relativ klar, dass die for...of
einfache Delegierung an die Iteratorfunktion erfolgt.
Dieser Ansatz ist nützlich, da es sehr einfach ist, eigene Iteratoren bereitzustellen. Ich könnte sagen, dass der Ansatz aufgrund seiner Abhängigkeit von der Definition einer Eigenschaft, bei der es zuvor keine gab, praktische Probleme hätte aufwerfen können, es sei denn, ich kann sagen, dass dies nicht der Fall ist, da die neue Eigenschaft im Wesentlichen ignoriert wird, es sei denn, Sie suchen absichtlich danach (d. H. es wird nicht in for...in
Schleifen als Schlüssel usw. angezeigt). Das ist also nicht der Fall.
Abgesehen von praktischen Problemen kann es als konzeptionell umstritten angesehen worden sein, alle Objekte mit einer neuen vordefinierten Eigenschaft zu beginnen oder implizit zu sagen, dass "jedes Objekt eine Sammlung ist".
3. Warum werden die Objekte nicht iterable
mit for...of
standardmäßig?
Ich vermute, dass dies eine Kombination aus:
- Das
iterable
standardmäßige Festlegen aller Objekte wurde möglicherweise als inakzeptabel angesehen, da eine Eigenschaft hinzugefügt wird, in der zuvor keine vorhanden war, oder weil ein Objekt (nicht unbedingt) eine Sammlung ist. Wie Felix bemerkt, "was bedeutet es, über eine Funktion oder ein Objekt mit regulären Ausdrücken zu iterieren"?
- Einfache Objekte können bereits mit iteriert werden
for...in
, und es ist nicht klar, was eine integrierte Iterator-Implementierung anders / besser als das vorhandene for...in
Verhalten hätte tun können. Selbst wenn # 1 falsch ist und das Hinzufügen der Eigenschaft akzeptabel war, wurde dies möglicherweise nicht als nützlich angesehen .
- Benutzer, die ihre Objekte erstellen möchten,
iterable
können dies einfach tun, indem sie die Symbol.iterator
Eigenschaft definieren.
- Die ES6 spec bietet auch eine Map - Typ, der ist
iterable
standardmäßig aktiviert und hat einige andere kleine Vorteile gegenüber einem einfachen Objekt als eine Verwendung Map
.
In der Referenzdokumentation finden Sie sogar ein Beispiel für Nummer 3:
var myIterable = {};
myIterable[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
};
for (var value of myIterable) {
console.log(value);
}
Angesichts der Tatsache, dass Objekte leicht erstellt werden können iterable
, dass sie bereits mit iteriert werden for...in
können und dass es wahrscheinlich keine klare Übereinstimmung darüber gibt, was ein Standardobjekt-Iterator tun soll (wenn das, was er tut, irgendwie anders sein soll als das, was er for...in
tut), erscheint es vernünftig genug, dass Objekte nicht iterable
standardmäßig erstellt wurden.
Beachten Sie, dass Ihr Beispielcode wie folgt umgeschrieben werden kann for...in
:
for (let levelOneKey in object) {
console.log(levelOneKey);
console.log(object[levelOneKey]);
var levelTwoObj = object[levelOneKey];
for (let levelTwoKey in levelTwoObj ) {
console.log(levelTwoKey);
console.log(levelTwoObj[levelTwoKey]);
}
}
... oder Sie können Ihr Objekt auch so erstellen, iterable
wie Sie es möchten, indem Sie Folgendes tun (oder Sie können alle Objekte iterable
erstellen, indem Sie sie Object.prototype[Symbol.iterator]
stattdessen zuweisen ):
obj = {
a: '1',
b: { something: 'else' },
c: 4,
d: { nested: { nestedAgain: true }}
};
obj[Symbol.iterator] = function() {
var keys = [];
var ref = this;
for (var key in this) {
keys.push(key);
}
return {
next: function() {
if (this._keys && this._obj && this._index < this._keys.length) {
var key = this._keys[this._index];
this._index++;
return { key: key, value: this._obj[key], done: false };
} else {
return { done: true };
}
},
_index: 0,
_keys: keys,
_obj: ref
};
};
Damit können Sie hier spielen (zumindest in Chrome): http://jsfiddle.net/rncr3ppz/5/
Bearbeiten
Und als Antwort auf Ihre aktualisierte Frage ist es iterable
mit dem Spread-Operator in ES6 möglich, ein in ein Array zu konvertieren .
Dies scheint jedoch noch nicht in Chrome zu funktionieren, oder zumindest kann ich es in meiner jsFiddle nicht zum Laufen bringen. Theoretisch sollte es so einfach sein wie:
var array = [...myIterable];
Symbol.iterator
Eigenschaft hat, ist iterierbar. Sie müssten also nur diese Eigenschaft implementieren. Eine mögliche Erklärung dafür, warum Objekte nicht iterierbar sind, könnte sein, dass dies implizieren würde, dass alles iterierbar ist, da alles ein Objekt ist (außer natürlich Primitiven). Was bedeutet es jedoch, über eine Funktion oder ein Objekt mit regulären Ausdrücken zu iterieren?