由Arfat Salman发布
本文旨在分析理解 Iterators 。 Iterators 是 JavaScript 中的新方法,可以用来循环任意集合。在ES6中登场的Iterators。因其可被广泛使用,并且已在多处场景派上用场,现已十分流行。
我们从概念上去理解iterators,通过实例讲述在何处使用。并且. We’ll also see some of its implementations in JavaScript.
介绍
如果你有下面的数组 ——
const myFavouriteAuthors = [
'Neal Stephenson',
'Arthur Clarke',
'Isaac Asimov',
'Robert Heinlein'
];
有时,你需要获取这个数组中每一个值,在屏幕上显示它们,操作它们,或者依据它们做些事情。如果我问你将如何处理?你会说,这很简单——我循环它们就是了,可以用for__, while__, for-of 或者 这些 循环方法中的任意一个。
各种循环数组的方法如果现在不是前面的数组,而是以自定的数据结构保存所有的authors,比如——
自定数据结构现在, myFavouriteAuthors
是一个包含了对象 allAuthors
的对象, allAuthors
包含了三个数组,对应key值为 fiction
, scienceFiction
,和 fantasy
。 现在如果需要遍历 myFavouriteAuthors
来获取所有的作者, 你的方法是什么? 你可以继续尝试一些循环组合来获取所有数据。
然而,如果你这样做 —
for (let author of myFavouriteAuthors) {
console.log(author)
}
// TypeError: {} is not iterable</pre>
你会得到一个该对象不是_可迭代的_TypeError。 我们来看看迭代是什么,以及我们如何使对象可迭代。 在本文的最后,你将知道如何在这种情况下,在 myFavouriteAuthors
上,在自定义对象上使用 for-of
循环。
Iterables(可迭代对象) and Iterators(迭代器)
在上一节中看到了问题所在。 没有方法可以简单得从我们的自定义对象中获取所有作者。 我们想要一种方法,通过它我们可以有序地呈现所有内部数据。
让我们在 myFavouriteAuthors
中添加一个返回所有作者的方法 getAllAuthors
。 像这样 ——
这是一个简单的方法。 它完成了获得所有作者的当下目标。 但是,这种实现可能会出现一些问题。 比如——
-
getAllAuthors
这个名字非常具体。 如果其他人正在创建他们自己的myFavouriteAuthors
,他们可能会将其命名为retrieveAllAuthors
。 -
作为开发人员,我们始终需要了解将返回所有数据的特定方法。 在这个案例里,它被命名为
getAllAuthors
。 -
getAllAuthors
返回包含所有作者的字符串数组。 如果另一个开发人员以这种格式返回一个对象数组会怎样——
[ {name: 'Agatha Christie'}, {name: 'J. K. Rowling'}, ... ]</pre>
开发人员必须知道,返回所有数据的方法的 确切名称和返回类型 。
如果我们制定一个 规则 ,该方法的 名称 及其 返回类型 , 是固定且不可更改的 ,这怎么办?
让我们将这个方法命名为—— **iteratorMethod **.
ECMA 采取了类似的步骤,来标准化循环自定义对象的过程。但是,ECMA使用 Symbol.iterator
而不是 iteratorMethod
。 Symbols 提供唯一且不能与其他属性名称冲突的名称。 此外, Symbol.iterator
将返回一个名为 iterator
的对象。 这个迭代器将有一个名为 next
的方法,它将返回一个包含key为 value
和 done
对象,
key value
中将包含当前值。 它可以是任何类型。 done
是布尔值。 它表示是否已获取所有值。
下图可能有助于建立 _ iterables _ , _ iterators _ 和 _ next _ 之间的关系。 这种关系称为迭代协议。
iterables, iterators, 和 next之间的关系。参考 Axel Rauschmayer博士 的 Exploring JS 一书 —
-
_ iterable _是一种数据结构,旨在使其元素可在公用环境被访问。 它通过实现一个key为
Symbol.iterator
的方法来实现。 该方法是 **iterators **的工厂。 也就是说,它将创建 **iterators **. -
iterator 是用于遍历数据结构中元素的指针。
使对象可迭代
正如我们在上一节中所学到的,我们需要实现一个名为 Symbol.iterator
的方法。我们将使用 computed property syntax 来设置这个key。举一个简短的例子 —
在第4行,我们创建iterator。 这是一个定义了 next
方法的对象。 next
方法根据 step
变量返回对应的值。 在第25行,我们取到 iterator
。 27行,我们调用 next
方法。 接下来我们继续调用,直到 done
变成'true`。
这正是 for-of
循环中发生的事情。 for-of
循环采用 **iterable **,并为其创建 _ iterator_ 。 不断调用 next()
直到 done
为真。
JavaScript中的 Iterables
很多东西在JavaScript中都是可迭代的。 它可能不是立等可见的,但如果仔细观察,是能看到iterables的。
这些都是可迭代的 ——
-
Arraysand TypedArrays
-
Strings —迭代每个字符或 Unicode code-points.
-
Maps —迭代 key-value 组合
-
Sets —迭代它们的元素
-
arguments
— 函数中一个类似数组的特殊变量 -
DOM 元素 (实现中)
其他在JS中使用iterables的 ——
-
for-of
循环 —for-of
循环需要迭代。 否则,它将抛出一个TypeError
.
for (const value of iterable) { ... }</pre>
- 数组的解构 ——由于可迭代而发生解构。 我们来看看如何实现。
代码:
const array = ['a', 'b', 'c', 'd', 'e'];
const [first, ,third, ,last] = array;</pre>
相当于
const array = ['a', 'b', 'c', 'd', 'e'];
const iterator = array[Symbol.iterator]();
const first = iterator.next().value
iterator.next().value // Since it was skipped, so it's not assigned
const third = iterator.next().value
iterator.next().value // Since it was skipped, so it's not assigned
const last = iterator.next().value</pre>
- 延展符 (…)
代码:
const array = ['a', 'b', 'c', 'd', 'e'];
const newArray = [1, ...array, 2, 3];</pre>
可以写成
const array = ['a', 'b', 'c', 'd', 'e'];
const iterator = array[Symbol.iterator]();
const newArray = [1];
for (let nextValue = iterator.next();nextValue.done !== true;nextValue = iterator.next()) {
newArray.push(nextValue.value);
}
newArray.push(2)
newArray.push(3)
-
Promise.all
和Promise.race
接受Promises上的迭代。 -
Maps and Sets
Map的构造函数将 [key,value]
形式的迭代器转换为Map,Set的构造函数将可迭代的元素转换为Set——
const map = new Map([[1, 'one'], [2, 'two']]);
map.get(1)
// one
const set = new Set(['a', 'b', 'c]);
set.has('c');
// true
- 要理解generator 函数,先理解迭代器。
让myFavouriteAuthors可迭代
这是一个使myFavouriteAuthors可迭代的实现方式。
可迭代的实现示例
根据本文的介绍,您可以轻松了解迭代器的工作方式。 可能有点难以理解它的逻辑,所以,我已经为代码写了注释。 但是,吸收和理解概念的最佳方法是让这些代码在浏览器或node中运行起来。
如有疑问,欢迎回复! 参考:
-
Dr Axel Rauschmayer 的 ExploringJS
网友评论