Ci proverò. Nota che non sono affiliato con ECMA e non ho visibilità sul loro processo decisionale, quindi non posso dire con certezza perché hanno o non hanno fatto nulla. Tuttavia, esporrò le mie ipotesi e farò del mio meglio.
1. Perché aggiungere un for...of
costrutto in primo luogo?
JavaScript include già un for...in
costrutto che può essere utilizzato per iterare le proprietà di un oggetto. Tuttavia, non è realmente un ciclo forEach , poiché enumera tutte le proprietà su un oggetto e tende a funzionare in modo prevedibile solo in casi semplici.
Si rompe in casi più complessi (incluso con gli array, dove il suo utilizzo tende ad essere scoraggiato o completamente offuscato dalle protezioni necessarie per un uso correttofor...in
con un array ). Puoi aggirare questo problema usando (tra le altre cose), ma è un po 'goffo e inelegante. hasOwnProperty
Pertanto, la mia ipotesi è che il for...of
costrutto venga aggiunto per affrontare le carenze associate al for...in
costrutto e fornire maggiore utilità e flessibilità durante l'iterazione delle cose. Le persone tendono a trattare for...in
come un forEach
ciclo che può essere generalmente applicato a qualsiasi raccolta e produce risultati sani in ogni possibile contesto, ma non è quello che succede. Il for...of
ciclo lo risolve.
Presumo anche che sia importante che il codice ES5 esistente venga eseguito sotto ES6 e produca lo stesso risultato di ES5, quindi non è possibile apportare modifiche di rilievo, ad esempio, al comportamento del for...in
costrutto.
2. Come for...of
funziona?
La documentazione di riferimento è utile per questa parte. Nello specifico, un oggetto viene considerato iterable
se definisce la Symbol.iterator
proprietà.
La definizione della proprietà dovrebbe essere una funzione che restituisce gli elementi nella raccolta, uno, per, uno e imposta un flag che indica se ci sono o meno più elementi da recuperare. Vengono fornite implementazioni predefinite per alcuni tipi di oggetto ed è relativamente chiaro che l'utilizzo di for...of
delegati semplicemente alla funzione iteratore.
Questo approccio è utile, poiché rende molto semplice fornire i propri iteratori. Potrei dire che l'approccio avrebbe potuto presentare problemi pratici a causa della sua dipendenza dalla definizione di una proprietà dove in precedenza non ce n'era, tranne da quello che posso dire che non è così in quanto la nuova proprietà viene essenzialmente ignorata a meno che tu non la cerchi deliberatamente non sarà presente nei for...in
loop come chiave, ecc.). Quindi non è così.
Pratiche non-questioni a parte, potrebbe essere stato considerato concettualmente controverso iniziare tutti gli oggetti con una nuova proprietà predefinita, o affermare implicitamente che "ogni oggetto è una collezione".
3. Perché gli oggetti non vengono iterable
utilizzati for...of
per impostazione predefinita?
La mia ipotesi è che questa sia una combinazione di:
- Rendere tutti gli oggetti
iterable
per impostazione predefinita potrebbe essere stato considerato inaccettabile perché aggiunge una proprietà dove prima non ce n'era, o perché un oggetto non è (necessariamente) una collezione. Come nota Felix, "cosa significa iterare su una funzione o un oggetto di espressione regolare"?
- Oggetti semplici possono già essere iterati usando
for...in
, e non è chiaro cosa avrebbe potuto fare un'implementazione iteratore integrato in modo diverso / migliore rispetto al for...in
comportamento esistente . Quindi, anche se il numero 1 è sbagliato e l'aggiunta della proprietà era accettabile, potrebbe non essere stata considerata utile .
- Gli utenti che desiderano creare i propri oggetti
iterable
possono farlo facilmente definendo la Symbol.iterator
proprietà.
- La specifica ES6 fornisce anche un tipo di mappa , che è
iterable
di default e presenta alcuni altri piccoli vantaggi rispetto all'uso di un oggetto semplice come file Map
.
C'è anche un esempio fornito per # 3 nella documentazione di riferimento:
var myIterable = {};
myIterable[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
};
for (var value of myIterable) {
console.log(value);
}
Dato che gli oggetti possono essere facilmente creati iterable
, che possono già essere iterati usando for...in
e che probabilmente non c'è un chiaro accordo su cosa dovrebbe fare un iteratore di oggetti predefinito (se ciò che fa deve essere in qualche modo diverso da ciò che for...in
fa), sembra ragionevole abbastanza che gli oggetti non fossero creati iterable
di default.
Tieni presente che il codice di esempio può essere riscritto utilizzando 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]);
}
}
... oppure puoi anche creare il tuo oggetto iterable
nel modo che preferisci facendo qualcosa di simile al seguente (oppure puoi creare tutti gli oggetti iterable
assegnando Object.prototype[Symbol.iterator]
invece a):
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
};
};
Puoi giocarci qui (in Chrome, a noleggio): http://jsfiddle.net/rncr3ppz/5/
modificare
E in risposta alla tua domanda aggiornata, sì, è possibile convertire un iterable
in un array, utilizzando l' operatore spread in ES6.
Tuttavia, questo non sembra ancora funzionare in Chrome, o almeno non riesco a farlo funzionare nel mio jsFiddle. In teoria dovrebbe essere semplice come:
var array = [...myIterable];
Symbol.iterator
proprietà è un iterabile. Quindi dovresti solo implementare quella proprietà. Una possibile spiegazione del motivo per cui gli oggetti non sono iterabili potrebbe essere che questo implicherebbe che tutto fosse iterabile, poiché tutto è un oggetto (tranne le primitive ovviamente). Tuttavia, cosa significa iterare su una funzione o un oggetto di espressione regolare?