# 迭代器模式
# 介绍
- 顺序访问一个集合
- 使用者无需知道集合的内部结构(封装)
# 示例
遍历对象数组。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="div1">
<a href="#">a1</a>
<a href="#">a2</a>
<a href="#">a3</a>
<a href="#">a4</a>
<a href="#">a5</a>
</div>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
<script>
var arr = [1, 2, 3]
var nodeList = document.getElementsByTagName('a')
var $a = $('a')
// 遍历数组
arr.forEach(function (item) {
console.log(item)
})
// 遍历 nodeList
var i, length = nodeList.length
for (i = 0; i < length; i++) {
console.log(nodeList[i])
}
// 遍历 $a
$a.each(function (key, elem) {
console.log(key, elem)
})
</script>
</body>
</html>
写一个三者通用的方法:
var arr = [1, 2, 3]
var nodeList = document.getElementsByTagName('a')
var $a = $('a')
function each(data) {
var $data = $(data) // 生成迭代器
$data.each(function (key, val) {
console.log(key, val)
})
}
each(arr)
each(nodeList)
each($a)
# UML 类图
# 代码演示
class Iterator {
constructor(container) {
this.list = container.list
this.index = 0
}
next() {
if(this.hasNext()) {
return this.list[this.index++]
}
}
hasNext() {
if(this.index >= this.list.length) {
return false
}
return true
}
}
class Container {
constructor(list) {
this.list = list
}
// 生成遍历器
getIterator() {
return new Iterator(this)
}
}
// 测试
const arr = [1, 2, 3, 4, 5, 6]
const container = new Container(arr)
const iterator = container.getIterator()
while(iterator.hasNext()) {
console.log(iterator.next())
}
# 场景
# jQuery each
// 如何能写一个方法来遍历这三种对象呢
var arr = [1, 2, 3]
var nodeList = document.getElementsByTagName('a')
var $a = $('a')
function each(data) {
var $data = $(data) // 生成迭代器
$data.each(function (key, val) {
console.log(key, val)
})
}
// 测试代码
each(arr)
each(nodeList)
each($a)
# ES6 Iterator
# es6 Iterator 为何存在
- ES6 语法中,有序集合的数据类型已经有很多
- Array Map Set String TypeArray arguments NodeList
- 需要有一个统一的遍历接口来遍历所有数据类型
- 注意,object 不是有序集合,可以用 Map 代替
# es6 Iterator 是什么
- 以上数据类型,都有 [Symbol.iterator] 属性
- 属性值是函数,执行函数返回一个迭代器
- 这个迭代器就有 next 方法可顺序迭代子元素
- 可运行 Array.prototype[Symbol.iterator] 来测试
# 代码演示
function each(data) {
// 生成遍历器
let iterator = data[Symbol.iterator]()
// console.log(iterator.next()) // 有数据时返回 { value: 1, done false }
// console.log(iterator.next())
// console.log(iterator.next())
// console.log(iterator.next())
// console.log(iterator.next()) // 无数据时返回 { value: undefined, done true }
let item = {done: false}
while(!item.done) {
item = iterator.next()
if(!item.done) {
console.log(item.value)
}
}
}
let arr = [1, 2, 3, 4]
let nodeList = document.getElementsByTagName('p')
let m = new Map()
m.set('a', 100)
m.set('b', 100)
each(arr)
each(nodeList)
each(m)
// Symbol.iterator 并不是人人都知道
// 也不是每个人都需要封装一个 each 方法
// 因此有了 for...of 语法
function each(data) {
for(let item of data) {
console.log(item)
}
}
# ES6 Iterator 与 Generator
- Iterator 的价值不限于上述几个类型的遍历
- 还有 Generator 函数的使用
- 即只要返回的数据符合 Iterator 接口的要求
- 即可使用 Iterator 语法,这就是迭代器模式
function* heGenerator() {
yield 'hello'
yield 'world'
return 'ending'
}
let hw = heGenerator()
// hw[Symbol.iterator] // 可以看到, Generator 函数返回的结果,也实现了 Iterator 接口
hw.next()
hw.next()
hw.next()
hw.next()
// 当然也可以用for...of
for(let v of heGenerator()) {
console.log(v)
}
# 设计原则验证
- 迭代器对象和目标对象分离
- 迭代器将使用者与目标对象隔离开
- 符合开放封闭原则
阅读量: