什么是迭代器
ES5中创建迭代器如下所示:
function createIterator(items) {
var i = 0
return {
next: function () {
var done = i >= items.length
var value = !done ? items[i++] : undefined
return {
done: done,
value: value
}
}
}
}
var iterator = createIterator([1, 2, 3])
console.log(iterator.next())
什么是生成器
生成器是一种返回迭代器的函数
每当招待完一条yield语句后函数就会自动停止执行
function *createIterator(){
yield 1
yield 2
yield 3
}
let iterator =createIterator()
console.log(iterator.next())
yield关键字可返回任何值或表达式
function* createIterator(items) {
for (let i = 0; i < items.length; i++) {
yield items[i]
}
}
let iterator = createIterator([1, 2, 3])
console.log(iterator.next())
yield关键字只可在生成器内部使用,在其它地方使用会导致程序抛出语法错误
所以下面例子是有错误的
function* createIterator(items) {
items.forEach(function (element) {
yield itemsm + 1
});
}
let iterator = createIterator([1, 2, 3])
console.log(iterator.next())
可迭代对象具有Symbol.iterator属性,可通过Symbol.iterator指定的函数来作用于一个附属对象的迭代器
由于生成器默认会有Symbol.iterator属性赋值,因此所有通过生成器创建的迭代器都是可迭代对象
如果将for of 语句用于不可迭代对象、null或undefined将会导致程序抛出错误
访问默认迭代器
let values = [1, 2, 3]
let iterator = values[Symbol.iterator]()
console.log(iterator.next())
检测一个对象是否为可迭代对象
function isIterable(obj) {
return typeof obj[Symbol.iterator] === "function"
}
console.log(isIterable([1, 2, 3]))
创建可迭代对象
let collection = {
items: [],
*[Symbol.iterator]() {
for (let item of this.items) {
yield item
}
}
}
collection.items.push(1)
collection.items.push(2)
collection.items.push(3)
for (let x of collection) {
console.log(x)
}
内建迭代器
数组、Map集合、Set集合,这3个对象都内建了以下三种迭代器
keys
values
entries
每个集合对象都有一个默认的迭代器,在for of循环中,如果没有显式指定则使用默认迭代器
数组和set集合默认的迭代器是values
Map集合默认的迭代器是entries
let tracking = new Set([123, 456, 789])
//与调用tracking.values方法相同
for (let num of tracking) {
console.log(num)
}
不仅仅字符串可以用for of来迭代,NodeList也可以
let divs = document.getElementsByTagName('div')
for (let div of divs) {
console.log(div)
}
展开运算符可作用于任何可迭代对象
let one = [1, 2, 3]
let two = [100, 101, 102]
let all = [0, ...one, ...two]
console.log(all)//[0, 1, 2, 3, 100, 101, 102]
高级迭代器
给迭代器传递参数
function* createIterator() {
let first = yield 1
let second = yield first + 2//4+2
yield second + 3//5+3
}
let iterator = createIterator()
console.log(iterator.next())
console.log(iterator.next(4))
console.log(iterator.next(5))
console.log(iterator.next())
在迭代器中抛出错误
function* createIterator() {
let first = yield 1
let second
try {
second = yield first + 2
} catch (error) {
second = 6
}
yield second + 3
}
let iterator = createIterator()
console.log(iterator.next())//{value: 1, done: false}
console.log(iterator.next(4)) //{value: 6, done: false}
console.log(iterator.throw(new Error('Boom')))//{value: 9, done: false}
console.log(iterator.next())//{value: undefined, done: true}
生成器返回语句
function* createIterator() {
yield 1
return;
yield 2
yield 3
}
let iterator = createIterator()
console.log(iterator.next())//{value: 1, done: false}
console.log(iterator.next())//{value: undefined, done: true}
function* createIterator() {
yield 1
return 42
}
let iterator = createIterator()
console.log(iterator.next())//{value: 1, done: false}
console.log(iterator.next())//{value: 42, done: true}
console.log(iterator.next())//{value: undefined, done: true}
有一点需要注意的是展开运算符与for of循环语句会直接忽略掉通过return语句指定的任何返回值,只要done一变为true就立即停止读取其它的值
异步任务执行器
向任务执行器传递参数
function run(taskDef) {
let task = taskDef()
let result = task.next()
function step() {
if (!result.done) {
result = task.next(result.value)
step()
}
}
step()
}
run(function* () {
let value = yield 1
console.log(value)
value = yield value + 3
console.log(value)
})
异步任务执行器
function run(taskDef) {
//创建一个无使用限制的迭代器
let task = taskDef()
// 开始执行任务
let result = task.next()
// 循环调用next函数
function step() {
// 如果任务未完成则继续执行
if (!result.done) {
if (typeof result.value === 'function') {
result.value(function (err, data) {
if (err) {
result = task.throw(err)
return;
}
result = task.next(data)
step()
})
} else {
result = task.next(result.value)
step()
}
}
}
// 开始执行迭代任务
step()
}
let fs = require('fs')
function readFile(fileName) {
return function (callback) {
fs.readFile(fileName, callback)
}
}
run(function* () {
let contents = yield readFile('config.json')
doSomethingWith(contents)
console.log('hello')
})