JavaScript 设计模式之迭代器模式

Author Avatar
Klein 8月 29, 2018

介绍

  • 顺序访问一个集合(通常是数组)
  • 使用者无需知道集合内部的结构

演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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)
}
}

let container = new Container([1, 2, 3, 4, 5, 6])
let iterator = container.getIterator()
while (iterator.hasNext()) {
console.log(iterator.next());
}

场景

jQuery each

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<p>jquery each</p>
<p>jquery each</p>
<p>jquery each</p>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>
let arr = [1, 2, 3, 4, 5, 6]
let nodeList = document.getElementsByTagName('p')
let $p = $('p')

// 要对这三个变量进行遍历,需要写三个遍历方法

// arr
arr.forEach(item => {
console.log(item);
});

// nodeList
for (let i = 0; i < nodeList.length; i++) {
console.log(nodeList[i]);
}

// $p
$p.each(function(key, p) {
console.log(key, p);
})


</script>

ES6 Iterator

1
2
3
4
5
6
7
8
9
10
// 如何写出一个方法来遍历这三种对象
function each(data) {
var $data = $(data)
$data.each(function(key, val) {
console.log(key, val);
})
}
each(arr)
each(nodeList)
each($a)
  • ES6 中有序的数据类型已经有很多
    • Array
    • Map
    • Set
    • String
    • TypedArray
    • arguments
    • nodeList
  • 需要有一个统一的遍历接口来遍历这些数据结构

注意 object 不是有序集合,可以用 Map 代替

ES6 Iterator 是什么

  • 以上的数据类型,都有 [Symbol.iterator] 属性
  • [Symbol.iterator] 属性值是函数,执行函数返回一个迭代器
  • 这个迭代器就有 next 方法可顺序迭代子元素
  • 可运行 Array.prototype.[Symbol.iterator] 来测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 使用 data[Symbol.iterator] 属性
function each(data) {
let iterator = data[Symbol.iterator]()

// 手动调用 next 方法
// 由于不知道迭代的数据的长度,所以不能手动调用 next 方法
// console.log(iterator.next())
// console.log(iterator.next())
// console.log(iterator.next())

let item = {done: false}
while(!item.done) {
item = iterator.next()
if (!item.done) {
console.log(item.value);
}
}
}

// 使用 for...of
function each(data) {
// 带有遍历器
for (const item of data) {
console.log(item);
}
}

let arr = [1, 2, 3, 4, 5, 6]
let nodeList = document.getElementsByTagName('p')
let m = new Map()
m.Set('a', 100)
m.Set('b', 100)

each(arr)
each(nodeList)
each(m)

ES6 Iterator 与 Generator

  • Iterator 的价值不限于上面提到的类型
  • 还有 Generator 函数的使用
  • 即只要返回的数据符合 Iterator 接口的要求
  • 即可使用 Iterator 语法,这就是迭代器模式

设计原则验证

  • 迭代器对象和目标对象的分离
  • 迭代器将使用者和目标对象隔离
  • 符合开放封闭原则