Я попробую. Обратите внимание, что я не связан с ECMA и не имею представления о процессе принятия ими решений, поэтому я не могу однозначно сказать, почему они что- то сделали или не сделали. Однако я выскажу свои предположения и сделаю все возможное.
1. Зачем вообще нужно добавлять for...of
конструкцию?
JavaScript уже включает в себя for...in
конструкцию, которая может использоваться для итерации свойств объекта. Однако на самом деле это не цикл forEach , поскольку он перечисляет все свойства объекта и работает предсказуемо только в простых случаях.
Он не работает в более сложных случаях (в том числе с массивами, когда его использование обычно либо не рекомендуется, либо полностью запутывается мерами безопасности, необходимыми для правильного использования for...in
с массивом ). Вы можете обойти это, используя (среди прочего), но это немного неуклюже и неэлегантно. hasOwnProperty
Поэтому я предполагаю, что for...of
конструкция добавляется для устранения недостатков, связанных с for...in
конструкцией, и обеспечения большей полезности и гибкости при итерации. Люди склонны рассматривать их for...in
как forEach
цикл, который может применяться к любой коллекции и давать разумные результаты в любом возможном контексте, но это не то, что происходит. В for...of
затруднительном цикле , который.
Я также предполагаю, что важно, чтобы существующий код ES5 работал под ES6 и давал тот же результат, что и под ES5, поэтому критические изменения не могут быть внесены, например, в поведение for...in
конструкции.
2. Как for...of
работает?
Справочная документация полезна для этой части. В частности, объект считается, iterable
если он определяет Symbol.iterator
свойство.
Определение свойства должно быть функцией, которая возвращает элементы в коллекции, один за другим, и устанавливает флаг, указывающий, есть ли еще элементы для выборки. Предопределенные реализации предусмотрены для некоторых типов объектов , и относительно ясно, что использование for...of
просто делегирует функцию итератора.
Этот подход полезен, поскольку он позволяет очень просто предоставить свои собственные итераторы. Я мог бы сказать, что этот подход мог вызвать практические проблемы из-за того, что он полагался на определение свойства там, где раньше его не было, за исключением того, что я могу сказать, что это не так, поскольку новое свойство по существу игнорируется, если вы специально не ищите его (т. Е. он не будет присутствовать в for...in
циклах как ключ и т. д.). Так что дело не в этом.
Если оставить в стороне практические проблемы, то, возможно, считалось концептуально спорным начинать все объекты с нового предварительно определенного свойства или неявно говорить, что «каждый объект является коллекцией».
3. Почему по умолчанию объекты не iterable
используются for...of
?
Я предполагаю , что это комбинация:
- Создание всех объектов
iterable
по умолчанию могло считаться неприемлемым, потому что оно добавляет свойство там, где раньше его не было, или потому что объект не (обязательно) является коллекцией. Как отмечает Феликс, «что значит перебирать функцию или объект регулярного выражения»?
- Простые объекты уже можно повторять с помощью
for...in
, и не ясно, что реализация встроенного итератора могла бы сделать иначе / лучше, чем существующее for...in
поведение. Таким образом, даже если №1 ошибочен и добавление свойства было приемлемым, оно могло оказаться бесполезным .
- Пользователи, которые хотят создать свои объекты,
iterable
могут легко сделать это, определив Symbol.iterator
свойство.
- Спецификация ES6 также предоставляет тип карты , который используется
iterable
по умолчанию и имеет некоторые другие небольшие преимущества по сравнению с использованием простого объекта в качестве Map
.
В справочной документации есть даже пример для №3:
var myIterable = {};
myIterable[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
};
for (var value of myIterable) {
console.log(value);
}
Учитывая, что объекты могут быть легко созданы iterable
, что они уже могут быть повторены с использованием for...in
и что, вероятно, нет четкого соглашения о том, что должен делать итератор объекта по умолчанию (если то, что он делает, должно как-то отличаться от того, что for...in
делает), это кажется разумным Достаточно того, что iterable
по умолчанию объекты не делались .
Обратите внимание, что ваш пример кода можно переписать, используя 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]);
}
}
... или вы также можете создать свой объект iterable
так, как хотите, выполнив что-то вроде следующего (или вы можете создать все объекты iterable
, назначив 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
};
};
Вы можете поиграть с этим здесь (в Chrome, если хотите): http://jsfiddle.net/rncr3ppz/5/
редактировать
И в ответ на ваш обновленный вопрос: да, можно преобразовать iterable
в массив, используя оператор распространения в ES6.
Однако, похоже, это еще не работает в Chrome, или, по крайней мере, я не могу заставить его работать в моем jsFiddle. Теоретически это должно быть так просто:
var array = [...myIterable];
Symbol.iterator
свойство, можно повторять. Так что вам просто нужно реализовать это свойство. Одно из возможных объяснений того, почему объекты не являются итерируемыми, может заключаться в том, что это будет означать, что все было итеративным, поскольку все является объектом (кроме, конечно, примитивов). Однако что означает перебор функции или объекта регулярного выражения?