迭代器 iterator,在 Javascript 中,迭代器是一个对象(也可称做为迭代器对象),它提供了一个 next() 方法,用来返回迭代序列中的下一项。函数
next 方法的定义,next 方法是一个函数,执行后返回一个对象包含两个属性:{ done: [boolean], value: [any] }
ui
function makeIterator(array) {
var nextIndex = 0
return {
next() {
return nextIndex < array.length ?
{ value: array[nextIndex++], done: false } :
{ done: true }
}
}
}
// iterator 是一个迭代器对象
var iterator = makeIterator([10, 20, 30])
iterator.next() // {value: 10, done: false}
iterator.next() // {value: 20, done: false}
iterator.next() // {value: 30, done: false}
iterator.next() // {done: true}
复制代码
可迭代对象必须实现一个 @@iterator 方法,也就是说在这个对象或者它的原型链上必须有一个方法名是 Symbol.iterator 的方法,当调用这个方法时它返回一个迭代器对象。this
可迭代对象的表现形式为,能够使用 for...of 循环
,解构赋值
,拓展运算符(spread)
,yield*
这些语法来调用 Symbol.iterator 函数。也就是说这些语法糖在被调用时本质上都是在调用 Symbol.iterator 函数。spa
String,Array,TypedArray,Map,Set,函数的arguments对象,NodeList对象都是内置的可迭代对象,他们的原型对象中都有一个 Symbol.iterator 方法。code
// 可迭代对象
let iterable = [10, 20, 30]
// 继承自原型链
Symbol.iterator in iterable // true
iterable.hasOwnProperty(Symbol.iterator) // false
for(let value of iterable){
console.log(value)
}
// 10
// 20
// 30
复制代码
字面量对象 let o = {}
默认没有 Symbol.iterator 方法,可是咱们在对象上自定义一个 @@iterator 方法,此时字面量对象也能够使用 for...of循环,拓展运算符等等语法糖。对象
// 字面量对象默认是不可迭代对象
// 自定义对
var myIterable = {}
myIterable[Symbol.iterator] = function(){
return {
arr: [10, 20, 30],
next: function(){
if(this.arr.length > 0){
return {value: this.arr.shift(), done: false}
}else{
return {done: true}
}
}
}
}
[...myIterable] // [10, 20, 30]
复制代码
生成器 generator,在 Javascript 中生成器是一个函数(也可称做生成器函数),它能够做为建立迭代器的工厂函数。生成器函数的返回值是一个迭代器对象,同时这个对象也是一个可迭代对象。继承
funtion* name(){ //statement }
这种声明方式能够定义一个生成器函数。ip
生成器函数的语法规则是,调用一个生成器函数并不会立刻执行它里面的语句,而是返回一个这个生成器的 迭代器(iterator)对象。当这个迭代器的 next() 方法被首次(后续)调用时,其内的语句会执行到第一个(后续)出现yield的位置为止,yield 后紧跟迭代器要返回的值。或者若是用的是 yield*(多了个星号),则表示将执行权移交给另外一个生成器函数(当前生成器暂停执行)。调用 next() 方法时,若是传入了参数,那么这个参数会做为上一条执行的 yield 语句的返回值。原型链
// 生成器函数
function* generator(i){
yield i + 1
var y = yield 'foo'
yield y
}
var iterator = generator(10) // 此时生成器函数不执行,返回一个迭代器
iterator.next() // {value: 11, done: false}
iterator.next() // {value 'foo', done: false}
iterator.next(10) // {value: 10, done: false},将10赋值给上一条 yield 'foo' 左侧的值,即 y = 10,返回 y
iterator.next() // {done: true}
复制代码
既然 生成器函数
能够建立 迭代器对象
,咱们来试着将前面的例子用生成器函数的形式重写试试看。generator
// 生成器函数
function* makeIterator(array) {
for (let i = 0; i < array.length; i++) {
yield array[i]
}
}
// 迭代器对象,实现和上文同样的功能
var iteratorByGen = makeIterator([10, 20, 30])
iteratorByGen.next() // {value: 10, done: false}
iteratorByGen.next() // {value: 20, done: false}
iteratorByGen.next() // {value: 30, done: false}
iteratorByGen.next() // {done: true}
复制代码
从上面的代码咱们能够看到,利用生成器函数来建立一个迭代器对象的方式相比于以前咱们普通函数建立的方式更加简洁,也更加清晰的代表调用生成器函数返回的是一个迭代器对象。除此以外还有什么区别呢。
上文已经提到,生成器函数返回的是一个 可迭代的迭代器对象
,这是什么意思呢?看下代码就明白了。
// 生成器函数建立的迭代器对象
Symbol.iterator in iteratorByGen // true
[...iteratorByGen] // [10, 20, 30]
// 普通函数建立的迭代器对象
Symbol.iterator in iterator // false
[...iterator] // Uncaught TypeError: iterator is not iterable
复制代码
综上所述,咱们能够肯定的说 生成器函数是建立迭代器对象的语法糖 ,经过生成器函数咱们能够用很简洁清晰的语法建立一个可迭代的迭代器对象。