JS 基础 -- Iterator & Generator

397 阅读2分钟

Iterator 迭代器

定义

迭代器是一个特殊对象,每个迭代器都有一个 next() 方法,每次调用都返回一个结果对象;结果对象有两个属性:一个是 value 表示当前迭代返回的值,另一个是 done 表示迭代是否完成。

ES5 实现

function createIterator(items) {
  var i = 0;

  return {
    next: function () {
      var done = i >= items.length;
      var value = done ? undefined : items[i++];

      return { value, done };
    }
  };
}

const iterator = createIterator([1, 2, 3]);
iterator.next(); // {value: 1, done: false}
iterator.next(); // {value: 2, done: false}
iterator.next(); // {value: 3, done: false}
iterator.next(); // {value: undefined, done: true}

js-base-fn

Generator 生成器

定义

生成器是返回迭代器的函数

使用

ES6 支持通过 * 与 yield 关键字实现

// 声明
function *createItrator() {
	yield 1;
	yield 2;
	yield 3;
}

// 表达式
const createIterator = function *(){
	yield 1;
	yield 2;
	yield 3;
}

const iterator = createIterator();
iterator.next(); // {value: 1, done: false}
iterator.next(); // {value: 2, done: false}
iterator.next(); // {value: 3, done: false}
iterator.next(); // {value: undefined, done: true}
  • yield 通过它来指定调用迭代器的 next() 方法时的返回值及返回顺序。
  • 每执行一条 yield 语句之后函数就会停止执行,直到再次调用迭代器的 next() 方法

yield 的使用限制

yield 只可以在生成器的内部使用,否则会会抛出错误,即便是生成器内部的函数里使用也是如此;

function *createIterator(items) {
	items.forEach(function() {
		yield item + 1; // 在内部函数中使用,导致语法错误
	})
}
// Uncaught SyntaxError: Unexpected identifier

yield 与 return 关键字一样,二者都不能穿透函数边界。

生成器对象的方法

ES6 函数方法的简写

let o = {
	*createIterator(items) {
		items.forEach(function() {
			yield item + 1; // 语法错误
		})
	}
}

可迭代对象和 for-of 循环

可迭代对象具有 Symbol.iterator 属性,Symbol.iterator 通过指定的函数返回一个作用域附属对象的迭代器。

const arr = [1, 2, 3];
for (let num of arr) {
	console.log(num);
}

for-of 循环代码通过调用 values 数组的 Symbol.iterator 方法来获取迭代器,随后迭代器的 next() 方法被多次调用,从其对象的 value 属性读取值并存储在变量 num 中

访问默认迭代器

let values = [1, 2, 3];
let iterator = values[Symbol.iterator]();

console.log(iterator.next()); // {value: 1, done: false}

判断是否为可迭代对象

function isIterable(object) {
	return typeof object[Symbol.iterator] === 'function'
}

创建可迭代对象

默认情况下,开发者定义的对象都是不可迭代对象,但如果给 Symbol.iterator 属性添加一个生成器,则可以将其变为可迭代对象

let collection  = {
	items: [],
	*[Symbol.iterator]() {
		for (let item of this.items) {
			yield item;
		}
	}
}

collection.item.push(1);
collection.item.push(2);
collection.item.push(3);

for (let x of collection) {
	console.log(x);
}