Итерация — это процесс непрерывного извлечения данных из набора данных в определенном порядке.
Так в чем же разница между итерацией и обходом?
В JavaScript итератор — это объект, который может вызвать next
метод для реализации итерации. возвращает объект An с двумя свойствами.
value
: следующее значение итерируемого объекта.done
: указывает, все ли данные были получены. false
означает, что данные еще есть, true
означает, что позже данных не будет.для генерации итераторов с помощью функции фабрики итераторов Symbol.iterator
в итерируемом объекте.
const arr = []console.log(arr)
константа обр = [1, 2, 3] const iter1 = arr[Symbol.iterator]() // Генерируем итератор с помощью функции фабрики итераторов `Symbol.iterator`. console.log(iter1) console.log(iter1.next()) console.log(iter1.next()) console.log(iter1.next()) console.log(iter1.next()) console.log('%c%s', 'color:red;font-size:24px;', '===============') const mymap = новая карта() mymap.set('имя', 'clz') mymap.set('возраст', 21) const iter2 = mymap[Symbol.iterator]() // Генерируем итератор с помощью функции фабрики итераторов `Symbol.iterator`. console.log(iter2) console.log(iter2.next()) console.log(iter2.next()) console.log(iter2.next())
Можно обнаружить, что итератор завершается после принятия последнего значения, то есть когда следующее value
итератора undefined
.
Однако приведенный выше оператор не очень точен. Он не завершается, когда следующее value
итератора undefined
. Вам также необходимо определить, действительно ли значение отсутствует или в итерируемом объекте есть значение, undefined
. Если в итерируемом объекте есть undefined
значение, он не будет завершен в этот момент.
const arr = [1, 2, 3, не определено] const iter1 = arr[Symbol.iterator]() // Генерируем итератор с помощью функции фабрики итераторов `Symbol.iterator`. console.log(iter1) console.log(iter1.next()) console.log(iter1.next()) console.log(iter1.next()) console.log(iter1.next()) console.log(iter1.next())
могут вызывать функцию фабрики итераторов несколько раз, не мешая друг другу, создавая несколько итераторов. Каждый итератор представляет собой однократный упорядоченный обход итерируемого объекта. Различные итераторы не мешают друг другу и будут перемещаться только по итерируемым объектам независимо друг от друга .
константа обр = [1, 2, 3] const iter1 = arr[Symbol.iterator]() // Генерируем итератор с помощью функции фабрики итераторов `Symbol.iterator`. const iter2 = arr[Symbol.iterator]() console.log('Итератор1:', iter1.next()) console.log('Итератор2:', iter2.next()) console.log('Итератор1:', iter1.next()) console.log('Итератор2:', iter2.next())
const arr = [1, 2, 3] const iter = arr[Symbol.iterator]() for (const i of iter) { console.log(i) // Вывод 1, 2, 3 последовательно }
Если итерируемый объект модифицируется в ходе итерации, то результат, полученный итератором, также будет модифицирован.
константа обр = [1, 2, 3] console.log(обр.) const iter = arr[Symbol.iterator]() console.log(iter.next()) обр[1] = 999 console.log(iter.next()) console.log(iter.next())
Когда мы выполним итерацию done: true
, будет ли сообщено об ошибке при вызове next
или ничего не будет возвращено?
Однако нет, итератор будет в завершенном, но не завершенном состоянии. done: true
означает, что он был завершен, но next
все еще может быть вызван в будущем, хотя результатом всегда будет { value: undefined, done: true }
. Вот почему говорят , что это сделано, но не сделано .
константа обр = [1, 2, 3] const iter = arr[Symbol.iterator]() console.log(iter.next()) console.log(iter.next()) console.log(iter.next()) console.log(iter.next()) console.log(iter.next()) console.log(iter.next())
Из приведенного выше примера мы можем знать, что итератор генерируется с помощью фабричной функции итератора Symbol.iterator
, поэтому нам нужно реализовать фабричную функцию итератора итератора, а затем итератор может вызвать next
метод, поэтому вам также необходимо реализовать next
метод. Что касается фабричной функции итератора, то она фактически возвращает экземпляр this
напрямую.
Пример счетчика:
класс Counter { конструктор (предел) { это.count = 1 this.limit = предел } следующий() { если (this.count <= this.limit) { возвращаться { сделано: ложь, значение: this.count++ } } еще { return {готово: правда, значение: неопределенное } } } [Символ.итератор]() { верните это }}
const counter = новый счетчик (3) const iter = counter[Symbol.iterator]() console.log(iter.next()) console.log(iter.next()) console.log(iter.next()) console.log(iter.next()) console.log(iter.next())
На первый взгляд проблемы нет, но если мы используем for-of
для обхода, мы можем найти проблему.
const counter = new Counter(3)for (пусть i счетчика) { console.log(i)}console.log('Еще одна итерация:')for (пусть i счетчика) { console.log(i)}
Использование цикла for-of
также делает его одноразовым. Это связано с тем, что count
является переменной этого экземпляра, поэтому в обеих итерациях используется одна и та же переменная. Однако после первого цикла переменная превысила предел, поэтому вы ничего не получите, используя цикл for-of
. снова. Результат был.
Вы можете поместить переменную count
в замыкание, а затем вернуть итератор через замыкание, чтобы каждый созданный итератор соответствовал новому счетчику.
класс Счетчик { конструктор (предел) { this.limit = предел } [Символ.итератор]() { пусть счетчик = 1 const limit = this.limit return { // Фабричная функция итератора должна возвращать объект со следующим методом, поскольку итерация фактически реализуется путем вызова следующего метода next() { если (счет <= предел) { возвращаться { сделано: ложь, значение: счетчик++ } } еще { return {готово: правда, значение: неопределенное } } } } }}
Test
const counter = new Counter(3)for (пусть i счетчика) { console.log(i)}console.log('Еще одна итерация:')for (пусть i счетчика) { console.log(i)}
аналогично использованию цикла for-of
. Итератор разумно вызывает next
метод. Когда итератор завершается раньше, он также вызывает метод return
.
[Символ.итератор]() { пусть счетчик = 1 const limit = this.limit return { // Фабричная функция итератора должна возвращать объект со следующим методом, поскольку итерация фактически реализуется путем вызова следующего метода next() { если (счет <= предел) { возвращаться { сделано: ложь, значение: счетчик++ } } еще { return {готово: правда, значение: неопределенное } } }, возвращаться() { console.log('Досрочное завершение итератора') вернуть {готово: правда} } }}
Test
const counter = new Counter(5)for (пусть i счетчика) { если (я === 3) { перерыв; } console.log(i)}
Если итератор не закрыт, вы можете продолжить итерацию с того места, где остановились . Итераторы массива не могут быть закрыты.
const arr = [1, 2, 3, 4, 5]const iter = arr[Symbol.iterator]()iter.return = function () { console.log('Досрочный выход из итератора') возвращаться { сделано: правда }}for (const i of iter) { консоль.log(я) если (я === 2) { перерыв }}for (const i of iter) { console.log(i)}