1. 迭代器 iterator
数组、Set、Map、NodeList对象可以使用for-of循环,因为有默认的迭代器属性。对象没有默认的迭代器。
可以使用Symbol.iterator来定义迭代器。
迭代器的出现背景:虽然循环语句简单,但是如果将多个循环嵌套则需要追踪多个变量,代码的复杂度会大大增加,一不小心就错误的使用了其他for循环的跟踪变量,从而导致程序出错,迭代器的出现旨在消除这种复杂性并减少循环中的错误。
迭代器是一种特殊的对象,每个迭代器对象都有一个next()方法,每次调用这个方法都会返回一个对象,这个对象包括两个属性:value,表示下一个将要返回的值,done:布尔值,当没有更多可返回的数据时返回true,并且后面都会返回true
2. 生成器 generator
生成器是一种返回迭代器的函数,函数中会用到新的关键字yield。格式如下
function *fname(){
yield 1;
yield 2;
}
let iterator = fname();
console.log(iterator.next().value); //1
生成器最有趣的地方是,每执行一条yield语句后函数就会自动停止执行,直到再次调用迭代器的next()方法才会继续执行。
yield的使用限制: yield关键字只可在生成器内部使用,在其他地方使用会导致程序抛出语法错误
function *createIterator(items){
items.forEach(function(item){
//语法错误
yield item+1;
})
}
它与return一样不能穿透函数边界
也可以通过函数表达式定义生成器
var createIterator = function *(){
..
yield 1;
}
还可以在对象里定义生成器
let o = {
createIterator: function *(){
yield 1;
}
}
let o = {
*createIterator(){
yield 1;
}
}
注意: 不能用箭头函数来创建生成器。为什么?箭头函数和普通的函数有哪些不同:
首先没有this、arguments、super和new.target绑定,这些值由外围最近一层非箭头函数决定。
不能通过new关键字调用,箭头函数没有construct方法
没有原型,不存在prototype这个属性
不可以改变this的绑定,函数内部的this值不可以改变(因为是外部的this),在函数的生命周期内始终保持一致。
3. 可迭代对象和for-of循环
可迭代对象具有Symbol.iterator属性,是一种与迭代器密切相关的对象
Symbol.iterator通过制定的函数可以返回一个作用于附属对象的迭代器:
let values = [1,2,3];
let iterator = values[Symbol.iterator]();
console.log(iterator.next());
在ES6中,所有的集合对象(数组、Set集合、Map集合)和字符串都是可迭代对象。for-of需要用到可迭代对象的这些功能。
for-of循环每次执行都会调用可迭代对象的next()方法,并将迭代器返回的结果对象的value属性存储在一个变量中,循环将持续执行这一过程直到返回对象的done属性的值为true。
将for-of循环用于不可迭代对象、null或undefined将会导致程序抛出错误。