阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 迭代器对象

迭代器对象

作者:陈川 阅读数:65675人阅读 分类: JavaScript

迭代器协议

ECMAScript 6 引入了迭代器协议,定义了标准的方式来产生一系列值。任何对象只要实现了迭代器协议,就可以成为迭代器对象。迭代器协议要求对象必须实现一个 next() 方法,该方法返回一个包含 valuedone 属性的对象。

const simpleIterator = {
  data: [1, 2, 3],
  index: 0,
  next() {
    return this.index < this.data.length
      ? { value: this.data[this.index++], done: false }
      : { value: undefined, done: true };
  }
};

console.log(simpleIterator.next()); // { value: 1, done: false }
console.log(simpleIterator.next()); // { value: 2, done: false }
console.log(simpleIterator.next()); // { value: 3, done: false }
console.log(simpleIterator.next()); // { value: undefined, done: true }

可迭代协议

可迭代协议允许 JavaScript 对象定义或定制它们的迭代行为。要成为可迭代对象,必须实现 @@iterator 方法,即对象或其原型链上必须有一个键为 Symbol.iterator 的属性。

const iterableObject = {
  items: ['a', 'b', 'c'],
  [Symbol.iterator]() {
    let index = 0;
    return {
      next: () => {
        return index < this.items.length
          ? { value: this.items[index++], done: false }
          : { value: undefined, done: true };
      }
    };
  }
};

for (const item of iterableObject) {
  console.log(item); // 依次输出 'a', 'b', 'c'
}

内置可迭代对象

ES6 中许多内置对象默认就是可迭代的,包括:

  • Array
  • String
  • Map
  • Set
  • TypedArray
  • arguments 对象
  • DOM NodeList
// 数组迭代
const arr = [10, 20, 30];
for (const num of arr) {
  console.log(num);
}

// 字符串迭代
const str = 'hello';
for (const char of str) {
  console.log(char);
}

// Map 迭代
const map = new Map([['a', 1], ['b', 2]]);
for (const [key, value] of map) {
  console.log(key, value);
}

生成器函数与迭代器

生成器函数是创建迭代器的更简洁方式。使用 function* 语法定义的函数返回一个生成器对象,该对象符合可迭代协议和迭代器协议。

function* numberGenerator() {
  yield 1;
  yield 2;
  yield 3;
}

const gen = numberGenerator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }

// 也可以直接用于迭代
for (const num of numberGenerator()) {
  console.log(num); // 依次输出 1, 2, 3
}

迭代器的高级用法

自定义迭代逻辑

可以创建复杂的迭代逻辑,比如实现一个斐波那契数列迭代器:

const fibonacci = {
  [Symbol.iterator]() {
    let prev = 0, curr = 1;
    return {
      next() {
        [prev, curr] = [curr, prev + curr];
        return { value: prev, done: false };
      }
    };
  }
};

// 获取前10个斐波那契数
let count = 0;
for (const num of fibonacci) {
  console.log(num);
  if (++count >= 10) break;
}

组合迭代器

可以通过组合多个迭代器来创建更复杂的迭代行为:

function* chain(...iterables) {
  for (const iterable of iterables) {
    yield* iterable;
  }
}

const combined = chain([1, 2], new Set([3, 4]), '56');
console.log([...combined]); // [1, 2, 3, 4, '5', '6']

迭代器与解构赋值

迭代器可以与解构赋值结合使用:

const [first, second] = [10, 20, 30];
console.log(first, second); // 10 20

const [head, ...tail] = 'hello';
console.log(head, tail); // 'h', ['e', 'l', 'l', 'o']

异步迭代器

ES2018 引入了异步迭代器,用于处理异步数据流。异步迭代器返回 Promise 对象:

const asyncIterable = {
  [Symbol.asyncIterator]() {
    let i = 0;
    return {
      next() {
        if (i < 3) {
          return Promise.resolve({ value: i++, done: false });
        }
        return Promise.resolve({ done: true });
      }
    };
  }
};

(async function() {
  for await (const num of asyncIterable) {
    console.log(num); // 依次输出 0, 1, 2
  }
})();

迭代器辅助方法

可以创建各种辅助方法来操作迭代器:

function take(iterable, count) {
  const iterator = iterable[Symbol.iterator]();
  return {
    [Symbol.iterator]() {
      return this;
    },
    next() {
      if (count-- <= 0) return { done: true };
      return iterator.next();
    }
  };
}

const numbers = [1, 2, 3, 4, 5];
console.log([...take(numbers, 3)]); // [1, 2, 3]

迭代器性能考虑

使用迭代器时需要注意性能问题。与直接数组访问相比,迭代器会产生额外的开销:

// 直接访问
const arr = [...Array(1000000).keys()];
console.time('direct');
let sum = 0;
for (let i = 0; i < arr.length; i++) {
  sum += arr[i];
}
console.timeEnd('direct');

// 使用迭代器
console.time('iterator');
sum = 0;
for (const num of arr) {
  sum += num;
}
console.timeEnd('iterator');

迭代器与生成器的错误处理

迭代器和生成器可以处理错误情况:

function* withErrorHandling() {
  try {
    yield 1;
    throw new Error('Something went wrong');
    yield 2;
  } catch (e) {
    yield 'Caught: ' + e.message;
  }
}

const gen = withErrorHandling();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 'Caught: Something went wrong', done: false }
console.log(gen.next()); // { value: undefined, done: true }

迭代器与状态管理

迭代器可以用于状态管理,保持内部状态:

function createCounter(start = 0, step = 1) {
  return {
    [Symbol.iterator]() {
      return this;
    },
    next() {
      const value = start;
      start += step;
      return { value, done: false };
    }
  };
}

const counter = createCounter(5, 2);
console.log(counter.next().value); // 5
console.log(counter.next().value); // 7
console.log(counter.next().value); // 9

迭代器与无限序列

迭代器特别适合表示无限序列:

function* naturalNumbers() {
  let n = 0;
  while (true) {
    yield n++;
  }
}

const numbers = naturalNumbers();
console.log(numbers.next().value); // 0
console.log(numbers.next().value); // 1
console.log(numbers.next().value); // 2
// 可以无限继续下去

迭代器与树结构遍历

迭代器可以用于实现复杂的树结构遍历:

class TreeNode {
  constructor(value, children = []) {
    this.value = value;
    this.children = children;
  }

  *[Symbol.iterator]() {
    yield this.value;
    for (const child of this.children) {
      yield* child;
    }
  }
}

const tree = new TreeNode(1, [
  new TreeNode(2, [
    new TreeNode(4),
    new TreeNode(5)
  ]),
  new TreeNode(3)
]);

console.log([...tree]); // [1, 2, 4, 5, 3]

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益,请来信告知我们删除。邮箱:cc@cccx.cn

上一篇:可迭代对象

下一篇:for...of循环原理

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌