Vou tentar. Observe que não sou afiliado à ECMA e não tenho visibilidade de seu processo de tomada de decisão, portanto, não posso dizer com certeza por que eles fizeram ou não fizeram nada. No entanto, vou expor minhas suposições e dar o meu melhor.
1. Por que adicionar uma for...of
construção em primeiro lugar?
JavaScript já inclui uma for...in
construção que pode ser usada para iterar as propriedades de um objeto. No entanto, não é realmente um loop forEach , já que enumera todas as propriedades em um objeto e tende a funcionar de forma previsível apenas em casos simples.
Ele quebra em casos mais complexos (incluindo com matrizes, onde seu uso tende a ser desencorajado ou completamente ofuscado pelas salvaguardas necessárias para o uso correto defor...in
uma matriz ). Você pode contornar isso usando (entre outras coisas), mas isso é um pouco desajeitado e deselegante. hasOwnProperty
Portanto, minha suposição é que o for...of
construto está sendo adicionado para resolver as deficiências associadas ao for...in
construto e fornecer maior utilidade e flexibilidade ao iterar coisas. As pessoas tendem a tratar for...in
como um forEach
loop que pode ser geralmente aplicado a qualquer coleção e produzir resultados sensatos em qualquer contexto possível, mas não é isso que acontece. O for...of
loop corrige isso.
Também presumo que seja importante que o código ES5 existente seja executado em ES6 e produza o mesmo resultado que em ES5, portanto, não podem ser feitas alterações significativas, por exemplo, no comportamento da for...in
construção.
2. Como for...of
funciona?
A documentação de referência é útil para esta parte. Especificamente, um objeto é considerado iterable
se definir a Symbol.iterator
propriedade.
A definição da propriedade deve ser uma função que retorna os itens na coleção, um, por, um, e define um sinalizador indicando se há ou não mais itens para buscar. Implementações predefinidas são fornecidas para alguns tipos de objeto e é relativamente claro que usar for...of
simplesmente delegados para a função iteradora.
Essa abordagem é útil, pois torna muito simples fornecer seus próprios iteradores. Eu poderia dizer que a abordagem poderia ter apresentado problemas práticos devido à sua confiança na definição de uma propriedade onde anteriormente não havia nenhuma, exceto pelo que posso dizer que não é o caso, pois a nova propriedade é essencialmente ignorada, a menos que você deliberadamente vá procurá-la (ou seja, não se apresentará em for...in
loops como uma chave, etc.). Então esse não é o caso.
Deixando de lado as questões práticas, pode ter sido considerado conceitualmente controverso iniciar todos os objetos com uma nova propriedade predefinida ou dizer implicitamente que "todo objeto é uma coleção".
3. Por que os objetos não são iterable
usados for...of
por padrão?
Meu palpite é que esta é uma combinação de:
- Fazendo todos os objetos
iterable
por padrão pode ter sido considerado inaceitável porque adiciona uma propriedade onde antes não havia nenhuma, ou porque um objeto não é (necessariamente) uma coleção. Como Felix observa, "o que significa iterar sobre uma função ou um objeto de expressão regular"?
- Objetos simples já podem ser iterados usando
for...in
, e não está claro o que uma implementação de iterador embutido poderia ter feito de forma diferente / melhor do que o for...in
comportamento existente . Portanto, mesmo se o nº 1 estiver errado e adicionar a propriedade for aceitável, pode não ter sido considerado útil .
- Os usuários que desejam fazer seus objetos
iterable
podem fazê-lo facilmente, definindo a Symbol.iterator
propriedade.
- A especificação ES6 também fornece um tipo de mapa , que é
iterable
por padrão e tem algumas outras pequenas vantagens sobre o uso de um objeto simples como um Map
.
Existe até um exemplo fornecido para o nº 3 na documentação de referência:
var myIterable = {};
myIterable[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
};
for (var value of myIterable) {
console.log(value);
}
Dado que os objetos podem ser facilmente feitos iterable
, que eles já podem ser iterados usando for...in
e que provavelmente não há um acordo claro sobre o que um iterador de objeto padrão deve fazer (se o que ele faz é para ser de alguma forma diferente do que for...in
faz), parece razoável o suficiente para que os objetos não fossem feitos iterable
por padrão.
Observe que seu código de exemplo pode ser reescrito usando 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 você também pode fazer seu objeto iterable
da maneira que quiser, fazendo algo como o seguinte (ou você pode fazer todos os objetos iterable
atribuindo a Object.prototype[Symbol.iterator]
):
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
};
};
Você pode brincar com isso aqui (no Chrome, pelo menos): http://jsfiddle.net/rncr3ppz/5/
Editar
E em resposta à sua pergunta atualizada, sim, é possível converter um iterable
em um array, usando o operador de propagação no ES6.
No entanto, isso não parece estar funcionando no Chrome ainda, ou pelo menos não consigo fazê-lo funcionar no meu jsFiddle. Em teoria, deveria ser tão simples como:
var array = [...myIterable];
Symbol.iterator
propriedade é iterável. Então, você apenas teria que implementar essa propriedade. Uma possível explicação de por que os objetos não são iteráveis poderia ser que isso implicaria que tudo era iterável, já que tudo é um objeto (exceto primitivos, é claro). No entanto, o que significa iterar sobre uma função ou um objeto de expressão regular?