迭代器
迭代器(iterator),是使用户在容器对象(container,例如链表或数组)上可以遍历访问的对象,使用该接口无需关心容器对象的内部实现细节。
从上面定义上来看,迭代器是帮助我们对某个数据结构进行遍历的对象
迭代器本身也是一个具体的对象,只不过这个对象需要符合迭代器协议,通俗的讲就是该对象必须含有个next方法,next方法返回一个拥有done(boolean)和value(一个具体的值或undefined)两个属性的对象。迭代结束done为true,否则为false
创建迭代器
下面我们写一个创建数组迭代器的方法
function createArrayIterator(arr) {
let index = 0
return {
next() {
if(index < arr.length) {
return { done: false, value: arr[index++] }
} else {
return { done: true, value: undefined }
}
}
}
}
let nums = [1,2,3]
let names = ['小明', '小红', '小芳']
let numsIterator = createArrayIterator(nums)
console.log(numsIterator.next()) // { done: false, value: 1 }
console.log(numsIterator.next()) // { done: false, value: 2 }
console.log(numsIterator.next()) // { done: false, value: 3 }
console.log(numsIterator.next()) // { done: true, value: undefined }
let namesIterator = createArrayIterator(names)
console.log(namesIterator.next()) // { done: false, value: '小明' }
console.log(namesIterator.next()) // { done: false, value: '小红' }
console.log(namesIterator.next()) // { done: false, value: '小芳' }
console.log(namesIterator.next()) // { done: true, value: undefined }
将一个非可迭代对象 转化为 可迭代对象
在这之前,我们可以提前考虑一下,为什么for of可以遍历数组而不能遍历对象,就是因为数组本身是有迭代器的,而对象则没有
从上面两个示例应该就能明白了,对象中没有 iterable 迭代器,因为不可for of 遍历
如果我们也想将一个非可迭代对象 转化为 可迭代对象 只需要在其中增加 Symbol.iterator 方法即可如下:
let info = {
name: 'wft',
age: 18,
height: 1.88,
// 增加 Symbol.iterator 方法 转为一个可迭代对象
[Symbol.iterator]() {
let values = Object.values(this)
let index = 0
let iterator = {
next() {
if(index < values.length) {
return { done: false, value: values[index++] }
} else {
return { done: true, value: undefined }
}
}
}
return iterator
}
}
let infoIterator = info[Symbol.iterator]()
console.log(infoIterator.next()) // { done: false, value: 'wft' }
console.log(infoIterator.next()) // { done: false, value: 18 }
console.log(infoIterator.next()) // { done: false, value: 1.88 }
console.log(infoIterator.next()) // { done: true, value: undefined }
for(let item of info) {
console.log(item)
}
//wft
//18
//1.88
注意:一定是 [Symbol.iterator] 规定好的,不可修改
arguements也是一个可迭代对象
是不是迭代对象就看有没有迭代器即可
function foo() {
console.log(arguments)
for(let item of arguments) {
console.log(item)
}
}
foo(1,2,3)
生成器
生成器函数:
- function 后面会跟上符号 *
- 代码的执行可以被yield控制(类似于断点)
- 生成器函数默认在执行时,返回一个生成器对象(并不会执行函数内部的代码)
- 要想执行函数内部的代码,需要生成器对象调用它的next方法操作
- 当调用next,执行内部函数代码时,当遇到yield时,就会中断执行
调用生成器函数,将返回生成器对象,但是并不会执行函数内部的代码,生成器对象中也有个next方法,调用next方法才会去执行方法内部的代码,遇到yield将终止执行 。示例如下:
// 生成器函数
function * fn() {
console.log(111)
yield
console.log(222)
yield
console.log(333)
}
// 调用生成器函数,返回生成器对象
// 注意:并没有执行fn中的代码
const generator = fn()
// 生成器也有一个 next 方法,调用next方法,将执行 fn 内部的代码,遇到 yield 将终止执行
generator.next() // 111
generator.next() // 222
generator.next() // 333
yield 叫产出,可以产出数据,也就是在 yield 后面跟上数据,那么它将会作为next方法的返回值,示例如下:
// 生成器函数
function * fn() {
// 第一次调用next方法,会执行这的代码...
yield '哈哈哈'
// 第二次调用next方法,会执行这的代码...
yield {name: 'wft', age: 18}
// 第三次次调用next方法,会执行这的代码...
}
const generator = fn()
console.log(generator.next()) // { value: '哈哈哈', done: false }
console.log(generator.next()) // { value: { name: 'wft', age: 18 }, done: false }
我们调用next方法的时候,也是可以传递参数的,那么这个参数就作为yield的返回值返回,示例如下:
// 生成器函数
function * fn() {
let num1 = yield '哈哈哈'
console.log(num1, 'num1') // 注意这里得调用第二次next方法的时候才会执行 --- 结果:123
let num2 = yield {name: 'wft', age: 18}
console.log(num2, 'num2') // 同样这里得调用第三次next方法的时候才会执行 --- 结果:456
}
const generator = fn()
console.log(generator.next()) // { value: '哈哈哈', done: false }
console.log(generator.next(123)) // { value: { name: 'wft', age: 18 }, done: false }
console.log(generator.next(456)) // { value: undefined, done: true }
使用生成器代替上面的迭代器
function * createArrayGenerator(arr) {
// 方式一:
for(let i = 0; i < arr.length; i++) {
yield arr[i]
}
// // 方式二:yield语法糖写法 等同于方式一
// yield * arr
}
let nums = [1,2,3]
// nums生成器对象
let numsGenerator = createArrayGenerator(nums)
console.log(numsGenerator.next()) // { value: 1, done: false }
console.log(numsGenerator.next()) // { value: 2, done: false }
console.log(numsGenerator.next()) // { value: 3, done: false }
console.log(numsGenerator.next()) // { value: undefined, done: true }