Je vais essayer. Notez que je ne suis pas affilié à l'ECMA et que je n'ai aucune visibilité sur leur processus de prise de décision, je ne peux donc pas dire avec certitude pourquoi ils ont ou n'ont rien fait. Cependant, je vais exposer mes hypothèses et faire de mon mieux.
1. Pourquoi ajouter une for...of
construction en premier lieu?
JavaScript inclut déjà une for...in
construction qui peut être utilisée pour itérer les propriétés d'un objet. Cependant, ce n'est pas vraiment une boucle forEach , car elle énumère toutes les propriétés d'un objet et a tendance à ne fonctionner de manière prévisible que dans des cas simples.
Il se décompose dans des cas plus complexes (y compris avec des baies, où son utilisation a tendance à être soit découragée, soit complètement obscurcie par les garanties nécessaires pour une utilisation for...in
avec une baie correcte ). Vous pouvez contourner cela en utilisant hasOwnProperty
(entre autres choses), mais c'est un peu maladroit et inélégant.
Donc, mon hypothèse est que le for...of
concept est ajouté pour combler les lacunes associées au for...in
concept et pour offrir une plus grande utilité et flexibilité lors de l'itération des choses. Les gens ont tendance à traiter for...in
comme unforEach
boucle qui peut généralement être appliquée à n'importe quelle collection et produire des résultats sains dans n'importe quel contexte possible, mais ce n'est pas ce qui se passe. La for...of
boucle corrige cela.
Je suppose également qu'il est important pour le code ES5 existant de fonctionner sous ES6 et de produire le même résultat que sous ES5, de sorte que des modifications radicales ne peuvent pas être apportées, par exemple, au comportement de la for...in
construction.
2. Comment ça for...of
marche?
La documentation de référence est utile pour cette partie. Plus précisément, un objet est considéré iterable
s'il définit la Symbol.iterator
propriété.
La définition de propriété doit être une fonction qui renvoie les éléments de la collection, un, par, un, et définit un indicateur indiquant s'il y a ou non plus d'éléments à récupérer. Des implémentations prédéfinies sont fournies pour certains types d'objets , et il est relativement clair que l'utilisation de for...of
délégués simplement à la fonction d'itérateur.
Cette approche est utile, car elle permet de fournir très simplement vos propres itérateurs. Je pourrais dire que l'approche aurait pu présenter des problèmes pratiques en raison de sa dépendance à la définition d'une propriété là où il n'y en avait pas auparavant, sauf de ce que je peux dire, ce n'est pas le cas car la nouvelle propriété est essentiellement ignorée à moins que vous ne la cherchiez délibérément (c.-à-d. il ne se présentera pas dans les for...in
boucles comme clé, etc.) Ce n'est donc pas le cas.
Mis à part les problèmes pratiques, il peut avoir été considéré comme controversé sur le plan conceptuel de commencer tous les objets avec une nouvelle propriété prédéfinie, ou de dire implicitement que «chaque objet est une collection».
3. Pourquoi les objets ne sont-ils pas iterable
utilisés for...of
par défaut?
Je suppose que c'est une combinaison de:
- La création de tous les objets
iterable
par défaut peut avoir été considérée comme inacceptable car elle ajoute une propriété là où il n'y en avait pas auparavant, ou parce qu'un objet n'est pas (nécessairement) une collection. Comme le note Felix, "que signifie itérer sur une fonction ou un objet d'expression régulière"?
- Les objets simples peuvent déjà être itérés à l'aide
for...in
, et on ne sait pas ce qu'une implémentation d'itérateur intégré aurait pu faire différemment / mieux que le for...in
comportement existant . Donc, même si # 1 est faux et que l'ajout de la propriété était acceptable, cela n'a peut-être pas été considéré comme utile .
- Les utilisateurs qui souhaitent créer leurs objets
iterable
peuvent le faire facilement, en définissant la Symbol.iterator
propriété.
- La spécification ES6 fournit également un type de carte , qui est
iterable
par défaut et présente d'autres petits avantages par rapport à l'utilisation d'un objet simple commeMap
.
Il y a même un exemple fourni pour # 3 dans la documentation de référence:
var myIterable = {};
myIterable[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
};
for (var value of myIterable) {
console.log(value);
}
Étant donné que les objets peuvent être facilement créés iterable
, qu'ils peuvent déjà être itérés en utilisant for...in
, et qu'il n'y a probablement pas d'accord clair sur ce qu'un itérateur d'objet par défaut devrait faire (si ce qu'il fait est censé être quelque peu différent de ce qu'il for...in
fait), cela semble raisonnable assez pour que les objets ne soient pas créés iterable
par défaut.
Notez que votre exemple de code peut être réécrit en utilisant 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]);
}
}
... ou vous pouvez également créer votre objet iterable
comme vous le souhaitez en faisant quelque chose comme ce qui suit (ou vous pouvez créer tous les objets iterable
en attribuant à la Object.prototype[Symbol.iterator]
place):
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
};
};
Vous pouvez jouer avec ça ici (dans Chrome, au bail): http://jsfiddle.net/rncr3ppz/5/
Éditer
Et en réponse à votre question mise à jour, oui, il est possible de convertir un iterable
en tableau, en utilisant l' opérateur de propagation dans ES6.
Cependant, cela ne semble pas encore fonctionner dans Chrome, ou du moins je ne peux pas le faire fonctionner dans mon jsFiddle. En théorie, cela devrait être aussi simple que:
var array = [...myIterable];
Symbol.iterator
propriété est un itérable. Il vous suffit donc de mettre en œuvre cette propriété. Une explication possible de la raison pour laquelle les objets ne sont pas itérables pourrait être que cela impliquerait que tout était itérable, puisque tout est un objet (sauf les primitifs bien sûr). Cependant, que signifie itérer sur une fonction ou un objet d'expression régulière?