迭代器是一个对象,是确使用户可在容器对象(container,例如链表或数组)上遍访的对象,使用该接口无需关心对象的内部实现细节。
迭代器有next属性,其对应的方法有如下的要求:
一个无参数或者一个参数的函数,返回一个应当拥有以下两个属性的对象:
const friends = ['lilei', 'kobe', 'james'];
let index = 0
const friendsIterator = {
next: function() {
if (index < friends.length)
return { done: false, value: friends[index++] }
} else {
return { done: true, value: friends[index++] }
}
}
console.log(friendsIterator.next().value);
console.log(friendsIterator.next().value);
console.log(friendsIterator.next().value);
当一个对象实现了iterable protocol协议时,它就是一个可迭代对象;它和迭代器是不一样的。这个对象的要求是必须实现 @@iterator 方法,在代码中我们使用 Symbol.iterator 访问该属性;
// 可迭代对象
const iteratorObj = {
names: ['leilei', 'tom', 'mark'],
index: 0,
[Symbol.iterator]: function() {
return {
// 这边使用箭头函数,以使this指向iteratorObj
next: () => {
if (this.index < this.names.length) {
return { done: false, value: this.names[this.index++] }
} else {
return { done: true, value: 'no name' }
}
}
}
}
}
// 获取迭代器
const iterator1 = iteratorObj[Symbol.iterator]()
console.log(iterator1.next());
console.log(iterator1.next());
console.log(iterator1.next());
console.log(iterator1.next());
事实上我们平时创建的很多原生对象已经实现了可迭代协议,会生成一个迭代器对象的:String、Array、Map、Set、arguments对象、NodeList集合;
生成器是ES6中新增的一种函数控制、使用的方案,它可以让我们更加灵活的控制函数什么时候继续执行、暂停执行等。
生成器函数也是一个函数,但是和普通的函数有一些区别:
function* getName() {
console.log('函数开始执行');
const value1 = 'tom'
console.log(value1)
yield value1
const value2 = 'mary'
console.log(value2);
yield value2
const value3 = 'tony'
console.log(value3)
yield value3
console.log('函数执行结束')
}
// 返回生成器
const iterator = getName()
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
// 函数开始执行
// tom
// { value: 'tom', done: false }
// mary
// { value: 'mary', done: false }
// tony
// { value: 'tony', done: false }
// '函数执行结束'
// { value: undefined, done: true }
注意:直接调用生成器函数是不会执行的,它会返回生成器。yield表示每次会在这儿停止。
其中,yield可以返回值。
生成器的next可以传递参数,而且这个参数会作为上一个yield语句的返回值;
function* sum() {
const value1 = 10;
const n1 = yield value1; // 这个n1的值为5
const value2 = 20 * n1
/**
* 注意此处的n接收的是next()传来的值,和上面的value2的值没有必然关联,当然我们也可以把第一
* 次的结果当作参数传入,视实际情况而定
*/
const n2 = yield value2 // 这个n2的值为10
const value3 = 30 * n2
yield value3
}
const iterator = sum()
console.log('第一次的值:', iterator.next());
console.log('第二次的值:', iterator.next(5)); // 传给第一次yield的返回值
console.log('第三次的值:', iterator.next(10)); // 传给第二次yield的返回值
// 第一次的值: {value: 10, done: false}
// 第二次的值: {value: 100, done: false}
// 第三次的值: {value: 300, done: false}
return传值后这个生成器函数就会结束,之后调用next不会继续生成值了;
function* sum() {
const value1 = 10;
const n1 = yield value1;
// 相当与在这执行了以下代码
// return 5
const value2 = 20 * n1
const n2 = yield value2
const value3 = 30 * n2
yield value3
}
const iterator = sum()
console.log('第一次的值:', iterator.next());
console.log('第二次的值:', iterator.return(5));
// 第一次的值: {value: 10, done: false}
// 第二次的值: {value: 5, done: true}
抛出异常后我们可以在生成器函数中捕获异常;
但是在catch语句中不能继续yield新的值了,但是可以在catch语句外使用yield继续中断函数的执行;
function* sum() {
const value1 = 10;
yield value1;
try {
const value2 = 20
yield 20
} catch (error) {
console.log(error);
// yield '哈哈,错了'
}
const value3 = 30
yield value3
}
const iterator2 = sum()
console.log('第一次的值:', iterator2.next());
console.log('第二次的值:', iterator2.next());
// 注意:throw紧跟着上个next()执行异常处理,所以要看好应该进行异常处理的位置
console.log('第三次的值:', iterator2.throw('有错误'));
console.log('第四次的值:', iterator2.next());
// 结果:
// 第一次的值: {value: 10, done: false}
// 第二次的值: {value: 20, done: false}
// 有错误
// 第三次的值: {value: 30, done: false}
// 第四次的值: {value: undefined, done: true}