Spróbuję. Zauważ, że nie jestem powiązany z ECMA i nie mam wglądu w ich proces podejmowania decyzji, więc nie mogę definitywnie powiedzieć, dlaczego oni coś zrobili lub nie zrobili. Jednak przedstawię swoje założenia i zrobię co w mojej mocy.
1. Po co w ogóle dodawać for...of
konstrukcję?
JavaScript zawiera już for...in
konstrukcję, której można użyć do iteracji właściwości obiektu. Jednak tak naprawdę nie jest to pętla forEach , ponieważ wylicza wszystkie właściwości obiektu i zwykle działa przewidywalnie tylko w prostych przypadkach.
Psuje się w bardziej złożonych przypadkach (w tym w przypadku tablic, gdzie jego użycie jest zwykle zniechęcane lub całkowicie zaciemniane przez zabezpieczenia potrzebne do prawidłowego użycia for...in
z macierzą ). Możesz to obejść, używając (między innymi), ale jest to trochę niezgrabne i nieeleganckie. hasOwnProperty
Dlatego zakładam, że for...of
konstrukcja jest dodawana w celu wyeliminowania braków związanych z for...in
konstrukcją i zapewnienia większej użyteczności i elastyczności podczas iteracji. Ludzie mają tendencję do traktowania for...in
jako forEach
pętli, które mogą być powszechnie stosowane do jakichkolwiek wyników zbiórki i produkty w rozsądnych ewentualnego związku, ale to nie to, co się dzieje. Te for...of
poprawki pętlę.
Zakładam również, że ważne jest, aby istniejący kod ES5 działał pod ES6 i dawał taki sam wynik jak w ES5, więc nie można wprowadzić istotnych zmian, na przykład w zachowaniu for...in
konstrukcji.
2. Jak to for...of
działa?
Dokumentacja referencyjna jest przydatna w tej części. W szczególności rozważany jest obiekt, iterable
jeśli definiuje on Symbol.iterator
właściwość.
Definicja właściwości powinna być funkcją, która zwraca elementy w kolekcji, jeden po drugim, i ustawia flagę wskazującą, czy jest więcej elementów do pobrania. Wstępnie zdefiniowane implementacje są dostępne dla niektórych typów obiektów i jest stosunkowo jasne, że użycie for...of
prostych delegatów do funkcji iteratora.
Takie podejście jest przydatne, ponieważ bardzo ułatwia dostarczanie własnych iteratorów. Mogę powiedzieć, że podejście to mogło spowodować problemy praktyczne ze względu na oparcie się na zdefiniowaniu nieruchomości, której wcześniej nie było, z wyjątkiem tego, co mogę powiedzieć, że tak nie jest, ponieważ nowa nieruchomość jest zasadniczo ignorowana, chyba że celowo jej szukasz (tj. nie będzie prezentował się w for...in
pętlach jako klucz itp.). Więc tak nie jest.
Odkładając na bok kwestie praktyczne, rozpoczęcie wszystkich obiektów od nowej, predefiniowanej właściwości lub pośrednie stwierdzenie, że „każdy obiekt jest zbiorem” mogło być koncepcyjnie kontrowersyjne.
3. Dlaczego obiekty nie iterable
używając for...of
domyślnie?
Moje przypuszczenie to, że jest to połączenie:
iterable
Domyślne ustawienie wszystkich obiektów mogło zostać uznane za niedopuszczalne, ponieważ dodaje właściwość, której wcześniej nie było, lub ponieważ obiekt nie jest (koniecznie) kolekcją. Jak zauważa Felix, „co to znaczy iterować po funkcji lub obiekcie wyrażenia regularnego”?
- Proste obiekty można już iterować przy użyciu
for...in
i nie jest jasne, co mogłaby zrobić wbudowana implementacja iteratora inaczej / lepiej niż istniejące for...in
zachowanie. Więc nawet jeśli nr 1 jest błędny i dodanie właściwości było do zaakceptowania, mogło nie zostać uznane za przydatne .
- Użytkownicy, którzy chcą tworzyć swoje obiekty,
iterable
mogą to łatwo zrobić, definiując Symbol.iterator
właściwość.
- Specyfikacja ES6 zapewnia również typ mapy , który jest
iterable
domyślny i ma kilka innych niewielkich zalet w porównaniu z użyciem zwykłego obiektu jako pliku Map
.
W dokumentacji referencyjnej podano nawet przykład nr 3:
var myIterable = {};
myIterable[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
};
for (var value of myIterable) {
console.log(value);
}
Biorąc pod uwagę, że obiekty można łatwo tworzyć iterable
, że można je już iterować przy użyciu for...in
i że prawdopodobnie nie ma jasnej zgody co do tego, co powinien robić domyślny iterator obiektów (jeśli to, co robi, ma się w jakiś sposób różnić od tego, co for...in
robi), wydaje się rozsądne wystarczy, że obiekty nie zostały utworzone iterable
domyślnie.
Zwróć uwagę, że Twój przykładowy kod można przepisać za pomocą 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]);
}
}
... lub możesz również stworzyć swój obiekt iterable
tak, jak chcesz, wykonując coś podobnego do następującego (lub możesz utworzyć wszystkie obiekty iterable
, przypisując Object.prototype[Symbol.iterator]
zamiast tego):
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
};
};
Możesz się tym bawić tutaj (w Chrome, przynajmniej): http://jsfiddle.net/rncr3ppz/5/
Edytować
W odpowiedzi na zaktualizowane pytanie tak, można przekonwertować an iterable
na tablicę, używając operatora spreadu w ES6.
Jednak wydaje się, że to jeszcze nie działa w Chrome, a przynajmniej nie mogę go uruchomić w moim jsFiddle. W teorii powinno być tak proste, jak:
var array = [...myIterable];
Symbol.iterator
właściwość, jest iterowalne. Musisz więc po prostu zaimplementować tę właściwość. Jedno możliwe wytłumaczenie dlaczego obiekty są nie mógł być iterable że oznaczałoby to wszystko było iterable, ponieważ wszystko jest obiektem (z wyjątkiem prymitywów oczywiście). Jednak co to znaczy iterować po funkcji lub obiekcie wyrażenia regularnego?