Клавиша / esc

Iterator.prototype.take()

Возвращает итератор, завершающийся после фиксированного числа шагов

Время чтения: 5 мин

Кратко

Скопировано

Iterator.prototype.take() создаёт итератор с гарантированным завершением. Метод возвращает итератор, который завершится после указанного числа успешных шагов итерации или раньше, в случае завершения исходного итератора. О том, что такое итератор, можно прочитать в статье «Итератор».

Пример

Скопировано

У нас есть коллекция фильмов и итератор для её обхода:

        
          
          const movies = [  'Братство кольца',  'Две крепости',  'Возвращение короля',  'Нежданное путешествие']const baseIterator = movies.values()
          const movies = [
  'Братство кольца',
  'Две крепости',
  'Возвращение короля',
  'Нежданное путешествие'
]

const baseIterator = movies.values()

        
        
          
        
      

Создадим из baseIterator новый итератор, для обхода только части коллекции, например для итерации по фильмам трилогии "Властелин Колец":

        
          
          const limitIterator = baseIterator.take(3)for (const item of limitIterator) {  console.log(item)}// Братство кольца// Две крепости// Возвращение короля
          const limitIterator = baseIterator.take(3)

for (const item of limitIterator) {
  console.log(item)
}
// Братство кольца
// Две крепости
// Возвращение короля

        
        
          
        
      

Как пишется

Скопировано

Iterator.prototype.take() принимает один обязательный аргумент — число, определяющее максимальное количество значений, которые может вернуть созданный итератор.

Iterator.prototype.take() возвращает новый итератор.

При выполнении метода произойдёт преобразование аргумента в целое положительное число, поэтому тип аргумента не ограничен Integer. Например, равнозначными будут значения:

  • 1
  • "1"
  • true
  • [1]
  • 1.7

Если передать 0 — метод вернёт завершённый итератор.

        
          
          const iterator = [ 'Трудности', 'перевода' ].values()const limitIterator = iterator.take(0)for (const item of limitIterator) {  console.log(item) // Не выполнится}
          const iterator = [ 'Трудности', 'перевода' ].values()
const limitIterator = iterator.take(0)

for (const item of limitIterator) {
  console.log(item) // Не выполнится
}

        
        
          
        
      

Если переданное значение не может быть преобразовано или является отрицательным числом, будет брошена ошибка RangeError. Попытка вызвать метод без аргумента так же приведёт к ошибке RangeError.

Как понять

Скопировано

Работая с итераторами можно столкнуться с ситуацией, когда необходимо ограничить количество получаемых значений. Например, это может понадобиться, когда итератор не имеет конечного состояния ({ done:true }). Такой итератор называют "бесконечный".

Рассмотрим пример. У нас есть функция-генератор паролей:

        
          
          function* passwordGenerator(length = 8) {  const charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()'  while (true) {    let password = '';    for (let i = 0; i < length; i++) {      const random = Math.floor(Math.random() * charset.length);      password += charset[random];    }    yield password;  }}
          function* passwordGenerator(length = 8) {
  const charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()'

  while (true) {
    let password = '';
    for (let i = 0; i < length; i++) {
      const random = Math.floor(Math.random() * charset.length);
      password += charset[random];
    }
    yield password;
  }
}

        
        
          
        
      

При вызове функции будет создан итератор, возвращающий строку-пароль:

        
          
          const passwords = passwordGenerator()console.log(passwords.next().value)// Z@1pivgSconsole.log(passwords.next().value)// PXoDm)B8
          const passwords = passwordGenerator()

console.log(passwords.next().value)
// Z@1pivgS
console.log(passwords.next().value)
// PXoDm)B8

        
        
          
        
      

passwords является итерируемым объектом, но его нельзя обойти с помощью for...of потому что это приведёт к бесконечному циклу. По этой же причине нельзя применить методы получения массива значений: Array.from() или Iterator.prototype.toArray().

Iterator.prototype.take() помогает получить итератор с ограниченным количеством итераций:

        
          
          const passwords = passwordGenerator()const limitPasswords = passwords.take(3)console.log(Array.from(limitPasswords))// [ '1QAg2NHv', 'L(46lQly', 'Vs9c)vWm' ]
          
const passwords = passwordGenerator()

const limitPasswords = passwords.take(3)

console.log(Array.from(limitPasswords))
// [ '1QAg2NHv', 'L(46lQly', 'Vs9c)vWm' ]

        
        
          
        
      

Итератор, созданный методом Iterator.prototype.take(), использует исходный итератор как источник данных. Это означает, что каждый шаг итерации выполняется за счёт вызова next() у исходного итератора. Если исходный итератор завершится, итерация созданного итератора также завершится.

Посмотрим как вызов next() у одного итератора влияет на состояние другого:

        
          
          const persons = [  'I Гретель',  'II Брунгильда',  'III Ирмгард',  'IV Адельхейд']const base = persons.values()const limit = base.take(3)base.next()console.log(limit.next().value)// II Брунгильдаconsole.log(base.next().value)// III Ирмгардconsole.log(Array.from(limit))// [ 'IV Адельхейд' ]
          const persons = [
  'I Гретель',
  'II Брунгильда',
  'III Ирмгард',
  'IV Адельхейд'
]

const base = persons.values()

const limit = base.take(3)

base.next()

console.log(limit.next().value)
// II Брунгильда

console.log(base.next().value)
// III Ирмгард

console.log(Array.from(limit))
// [ 'IV Адельхейд' ]

        
        
          
        
      

Можно сделать выводы:

  • Iterator.prototype.take() не создаёт независимую копию с доступом к части значений исходного итератора;
  • Каждый вызов next() у одного итератора влияет на состояние другого, потому что они делят общее состояние итерации.

Подсказки

Скопировано

Если итератор не является наследником глобального объекта Iterator, метод take() можно вызвать через call(). Это пригодится для итераторов, реализованных вручную.

Допустим имеется функция для создания итератора цветов: #0000FF, #00FF00, #FFFF00, #FF0000

        
          
          function createColorIterator() {  const colors = ['001', '010', '110', '100']  let index = 0  return {    next() {      if (index === colors.length) {        return { done: true }      }      const rgb = colors[index++].split('').map(c => Number(c) * 255)      const value = `rgb(${rgb.join(',')})`      return { value, done: false }    }  }}const colors = createColorIterator()
          function createColorIterator() {
  const colors = ['001', '010', '110', '100']
  let index = 0

  return {
    next() {
      if (index === colors.length) {
        return { done: true }
      }
      const rgb = colors[index++].split('').map(c => Number(c) * 255)
      const value = `rgb(${rgb.join(',')})`
      return { value, done: false }
    }
  }
}

const colors = createColorIterator()

        
        
          
        
      

Вызов colors.take(2) приведёт к ошибке TypeError, так как итератор colors не наследует методы Iterator.prototype:

        
          
          console.log(colors.take)// undefinedconsole.log(colors instanceof Iterator)// false
          console.log(colors.take)
// undefined
console.log(colors instanceof Iterator)
// false

        
        
          
        
      

Вызвать take() можно с помощью call():

        
          
          const limitColors = Iterator.prototype.take.call(colors, 2)for (const color of limitColors) {  console.log(color)}// rgb(0,0,255)// rgb(0,255,0)
          const limitColors = Iterator.prototype.take.call(colors, 2)

for (const color of limitColors) {
  console.log(color)
}
// rgb(0,0,255)
// rgb(0,255,0)

        
        
          
        
      
Поддержка в браузерах:
  • Chrome 122, поддерживается
  • Edge 122, поддерживается
  • Firefox 131, поддерживается
  • Safari 18.4, поддерживается
О Baseline