一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第18天,点击查看活动详情。
迭代器描述
迭代器模式 (Iterator Pattern)用于顺序地访问聚合对象内部的元素,又无需知道对象内部结构。使用了迭代器之后,使用者不需要关心对象的内部构造,就可以按序访问其中的每个元素。
因为 JavaScript
已经内置了迭代器的实现,在某些个很老的语言中,使用者可能会为了实现迭代器而烦恼,但是在 JavaScript 中则完全不用担心。
前端代码的迭代器
1、JavaScript 原生支持
随着 JavaScript
的 ECMAScript
的发展,出现了很多方便好用的API,比如 Array
上的 filter
、forEach
、reduce
、flat
等,还有 Map
、Set
、String
等数据结构,也提供了原生的迭代器支持。
var arr = [1, 'red', false, 3.14]
arr.forEach(function(item) {
console.log(item)
})
2、jQuery
中的 $.each
遍历方法
var arr = ['hello', 'world', '!']
function callback(index, item) {
console.log(index, item)
}
$.each(arr, callback)
3、ES6 迭代器
-
ES6
规定,默认的迭代器部署在对应数据结构的Symbol.iterator
属性上,如果一个数据结构具有Symbol.iterator
属性,就被视为可遍历的,就可以用for...of
循环遍历它的成员。也就是说,for...of
循环内部调用的是数据结构的Symbol.iterator
方法 -
for-of
循环可以使用的范围包括Array
、Set
、Map
结构、上文提到的类数组结构、Generator
对象,以及字符串 -
通过
for-of
可以使用Symbol.iterator
这个属性提供的迭代器可以遍历对应数据结构,如果对没有提供Symbol.iterator
的目标使用for-of
则会抛错:
var foo = { a: 1 }
for (var key of foo) {
console.log(key)
}
// 这样写报错
所以我们需要自己给foo定义一个迭代器,让其支持for-of
循环:
var foo = {
a: 1,
[Symbol.iterator]: function() {
var valArr = [
{ value: 1, done: false },
{ value: 2, done: false },
{ value: 'other', done: false },
{ value: undefined, done: true }
]
return {
next: function() {
return valArr.shift()
}
}
}
}
for (var key of foo) {
console.log(key)
}
// 输出: 1
// 输出: 2
// 输出: other
实现一个迭代器
迭代器模式解决了以下问题:
- 提供一致的遍历各种数据结构的方式,而不用了解数据的内部结构
- 提供遍历容器(集合)的能力而无需改变容器的接口
一个迭代器通常需要实现以下接口:
- hasNext():判断迭代是否结束,返回Boolean
- next():查找并返回下一个元素
为Javascript的数组实现一个迭代器可以这么写:
const item = [1, 'red', false, 3.14];
function Iterator(items) {
this.items = items;
this.index = 0;
}
Iterator.prototype = {
hasNext: function () {
return this.index < this.items.length;
},
next: function () {
return this.items[this.index++];
}
}
// 使用
const iterator = new Iterator(item);
while(iterator.hasNext()){
console.log(iterator.next());
}
//输出:1, red, false, 3.14
ES6提供了更简单的迭代循环语法 for...of,使用该语法的前提是操作对象需要实现 可迭代协议(The iterable protocol),简单说就是该对象有个Key为 Symbol.iterator 的方法,该方法返回一个iterator对象。
比如我们实现一个 Range 类用于在某个数字区间进行迭代:
function Range(start, end) {
return {
[Symbol.iterator]: function () {
return {
next() {
if (start < end) {
return { value: start++, done: false };
}
return { done: true, value: end };
}
}
}
}
}
// 使用
for (num of Range(1, 5)) {
console.log(num);
}
// 输出:1, 2, 3, 4