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...ofconstrução em primeiro lugar?
JavaScript já inclui uma for...inconstruçã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...inconstruto e fornecer maior utilidade e flexibilidade ao iterar coisas. As pessoas tendem a tratar for...incomo um forEachloop 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...ofloop 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...inconstrução.
2. Como for...offunciona?
A documentação de referência é útil para esta parte. Especificamente, um objeto é considerado iterablese definir a Symbol.iteratorpropriedade.
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...ofsimplesmente 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...inloops 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 iterableusados for...ofpor 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...incomportamento 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
iterablepodem fazê-lo facilmente, definindo a Symbol.iteratorpropriedade.
- 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...ine 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...infaz), parece razoável o suficiente para que os objetos não fossem feitos iterablepor 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 iterableda maneira que quiser, fazendo algo como o seguinte (ou você pode fazer todos os objetos iterableatribuindo 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 iterableem 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.iteratorpropriedade é 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?