iterator的基本介绍
概念
iterator
(遍历器) 是一种接口,为不同的数据结构提供统一的访问机制。
任何数据结构只要部署了iterator
接口,就可以完成遍历操作
作用
- 为各种数据结构, 提供一个统一的、简便的访问接口
- 使得数据结构的成员能够按照某种次序排列
ES6
创建了一种新的遍历命令for ... of
循环,iterator
接口主要供for ... of
消费
遍历器对象的属性
next()
方法: 必须部署,用于进行数据结构的遍历, 返回包含value
和done
的对象return()
方法: 可选, 循环中断或出错时会调用, 必须返回一个对象throw()
方法: 可选, 配合Generator
函数使用, 一般用不到
遍历过程
- 创建一个指针对象(包含
next
方法), 指向当前数据结构的起始位置 - 第一次调用指针对象的
next
方法, 将指针指向数据结构的第一个成员 - 第二次调用指针对象的
next
方法, 将指针指向数据结构的第二个成员 - 不断调用指针对象的
next
方法, 直到指向数据结构的结束位置
每次调用next
方法, 都会返回数据结构当前成员的信息, 信息的本质是一个对象, 其中包含了value
和done
两个属性:
value
属性是当前成员的值, done
属性代表遍历是否结束 Boolean
类型
如果 done
的值为 false
, 那么可以只返回 value
属性
如果 value
的值为 undefined
, 那么可以只返回 done
属性
原生用于iterator接口的数据
Array
Arguments
String
Set
Map
NodeList
默认调用iterator接口的场合
- 解构赋值
- 拓展运算符(…)
yield*
后面跟的是一个可遍历的结构, 它会调用该结构的遍历器接口- 数组的遍历都会调用遍历器接口
基础示例
var arr = [
'EcmaScript',
'NodeJs',
'TypeScipt',
'uni-app',
'vue',
]
var it = arr[Symbol.iterator]() // 得到包含next方法的指针对象
console.log(it.next()) // {value: "EcmaScript", done: false}
console.log(it.next()) // {value: "NodeJs", done: false}
console.log(it.next()) // {value: "TypeScipt", done: false}
console.log(it.next()) // {value: "uni-app", done: false}
console.log(it.next()) // {value: "vue", done: false}
console.log(it.next()) // {value: undefined, done: true} 遍历结束 value的值为undefined done的值为true
自定义部署iterator接口
默认的
Iterator
接口部署在数据结构的Symbol.iterator
属性
或者说,一个数据结构只要具有Symbol.iterator
属性,就可以认为是"可遍历的"(iterable
)
普通对象
var web = {
name : '前端',
ctn : [
'EcmaScript',
'NodeJs',
'TypeScipt',
'uni-app',
'vue',
],
[Symbol.iterator]() {
let index = 0
let _this = this
return {
next : function () {
if (index < _this.ctn.length) {
const result = {
value: _this.ctn[index],
done: false
}
index ++
return result
}
else {
return {
value : undefined,
done: true
}
}
}
}
}
}
for (let item of web) {
console.log(item) // EcmaScript NodeJs TypeScipt uni-app vue
}
类数组对象
类数组对象(存在
数值键名
和length
属性),部署Iterator
接口,有一个简便方法,就是Symbol.iterator
方法直接引用数组的Iterator
接口
for...of
遍历此类对象时, 只能遍历数值键名
, 无法遍历字符串键名
var obj = {
0: 'a',
1: 'b',
2: 'c',
a: 111,
length: 3,
[Symbol.iterator] : Array.prototype[Symbol.iterator]
}
for (let item of obj) {
console.log(item) // a b c
}
类部署iterator接口的实例
class RangeIterator {
constructor(start, stop) {
this.start = start
this.stop = stop
}
[Symbol.iterator] () {
return this
}
next() {
var start = this.start
if (this.start <= this.stop) {
this.start += 1
return {
value: start,
done: false
}
} else {
return {
value: undefined,
done: true
}
}
}
}
var rangeIterator = new RangeIterator(1, 3)
var it = rangeIterator[Symbol.iterator]()
console.log(it.next()) // {value: 1, done: false}
console.log(it.next()) // {value: 2, done: false}
console.log(it.next()) // {value: 3, done: false}
console.log(it.next()) // {value: undefined, done: true}
遍历器实现指针结构
function Obj(value) {
this.value = value
this.next = null
}
Obj.prototype[Symbol.iterator] = function () {
var current = this
function next () {
if (current) {
var value = current.value
current = current.next
return {
value: value,
done: false
}
} else {
return {
value: undefined,
done: true
}
}
}
return {
next : next
}
}
var one = new Obj(1)
var two = new Obj(2)
var three = new Obj(3)
one.next = two
two.next = three
var it = one[Symbol.iterator]()
console.log(it.next()) // {value: 1, done: false}
console.log(it.next()) // {value: 2, done: false}
console.log(it.next()) // {value: 3, done: false}
console.log(it.next()) // {value: undefined, done: true}
for…in和for…of
for...in
循环遍历得到的是数据结构的键名for...of
循环得到遍历的是数据结构的键值for...in
循环主要是为遍历对象而设计的, 不适用于遍历数组
for…in循环的几个缺点
for...in
循环遍历数组时, 得到的键名的类型是string
类型, 而数组的键名是number
类型for...in
循环不仅遍历数字键名, 还会遍历手动添加的其他键, 甚至包括原型链上的键名- 某些情况下,
for...in
循环会以任意顺序遍历键名
var obj = {
0 : 'a',
1 : 'b',
2 : 'c',
length: 3,
[Symbol.iterator] : Array.prototype[Symbol.iterator]
}
obj.foo = '123'
for(let prop in obj) {
console.log(prop) // 0 1 2 length foo
}
for(let item of obj) {
console.log(item) // 0 1 2
}
for…of循环的几个优点
- 有着同
for...in
一样的简洁语法, 但是没有for...in
那些缺点 - 不同用于
forEach
方法, 它可以与break
、continue
和return
配合使用 - 提供了遍历所有数据结构的统一操作接口。