log(typeof new Array("Nnamdi", "Chidume")[Symbol.iterator]);
// function
这是我们可以对它使用for...of循环的原因。
const arr = ["Chidume", "Nnamdi", "loves", "JS"]
for(const a of arr) {
log(a)
}
// It logs:
// Chidume
// Nnamdi
// loves
// JS
const arr = new Array("Chidume", "Nnamdi", "loves", "JS")
for(const a of arr) {
log(a)
}
// It logs:
// Chidume
// Nnamdi
// loves
// JS
for…of: String
字符串也是可迭代的。
const myname = "Chidume Nnamdi"
for(const a of myname) {
log(a)
}
// It logs:
// C
// h
// i
// d
// u
// m
// e
//
// N
// n
// a
// m
// d
// i
const str = new String("The Young")
for(const a of str) {
log(a)
}
// 打印结果是:
// T
// h
// e
//
// Y
// o
// u
// n
// g
Object.prototype[Symbol.iterator] = function() {
let properties = Object.keys(this)
let count = 0
let isdone = false
let next = () => {
let value = this[properties[count]]
if (count == properties.length) {
isdone = true
}
count++
return { done: isdone, value }
}
return { next }
}
理解 JavaScript 中的循环
在
JavaScript
的世界中,我们可以使用很多种循环表达式:while
表达式do...while
表达式for
表达式for...in
表达式for...of
表达式所有这些表达式都有一个基本的功能:它们会重复一件事情直到一个具体的条件出现。
在这篇文章中,我们将深入
for...of
表达式,去了解它是如何工作的,以及在我们的应用中可以使用它来优化代码的地方。for…of
for...of
是一种for
表达式,用来迭代iterables(iterable objects)
直到终止条件出现。下面是一个基础的例子:
使用比
for
循环更好的代码,我们遍历了arr
数组。你知道如果我们使用
for
循环,我们将必须使用数学和逻辑去判断何时我们将会达到myname
的末尾并且停止循环。但是正如你所见的,使用for...of
循环之后,我们将会避免这些烦人的事情。for...of
有以下通用的使用方法:variable
- 保存每次迭代过程中的迭代对象的属性值iterable
- 我们进行迭代的对象Iterables(可迭代的对象) and Iterator(迭代器)
在
for...of
循环的定义中,我们说它是“迭代 iterables(iterable objects)”。这个定义告诉我们for...of
循环只能用于可迭代对象。那么, 什么是可迭代的对象(
iterables
)?简单来说的话,可迭代对象(Iterables)是可以用于迭代的对象。在
ES6
中增加了几个特性。这些特性包含了新的协议,其中就有迭代器(Iterator)协议和可迭代(Iterable)协议。参考
MDN
的描述,“可迭代协议允许JS
对象定义或者修改它们的迭代行为,比如哪些值可以被for...of
循环到。”同时“为了变成可迭代的,对象必须实现@@iterator
方法,这意味着这个对象(或者其原型链上的对象)必须包含一个可以使用Symbol.iterator
常量访问的@@iterator
的属性”说人话就是,对于可以使用
for...of
循环的对象来说,它必须是可迭代的,换句话就是它必须有@@iterator
属性。这就是可迭代协议。所以当对象有
@@iterator
属性的时候就可以被for...of
循环迭代,@@iterator
方法被for...of
调用,并且返回一个迭代器。同时,迭代器协议定义了一种对象中的值如何返回的方式。一个迭代器对象必须实现
next
方法,next 方法需要遵循以下准则:举个例子:
基本上,
@@iterator
方法回返回一个迭代器,for...of
循环正是使用这个迭代器去循环操作目标对象从而得到值。因此,如果一个对象没有@@iterator
方法同时这个返回值是一个迭代器,for...of
循环将不会生效。内置的可迭代对象有有以下这些:
注意,Object 不是可迭代的。如果我们尝试使用
for...of
去迭代对象的属性:将会抛出一个异常:
我们可以用下面的方式检查一个对象是否可迭代:
看到了吧,打印的结果是
function
, 这意味着@@iterator
属性存在,并且是函数类型。如果我们在 Object 上面进行尝试:哇!
undefined
表示不存在。for…of: Array
数组是可迭代对象。
这是我们可以对它使用
for...of
循环的原因。for…of: String
字符串也是可迭代的。
for…of: Map类型
for…of: Set类型
for…of: TypedArray类型
for…of: arguments对象
arguments对象是可遍历的吗?我们先来看:
答案出来了。如果进一步探讨,arguments其实是IArguments类型的对象,而且实现了IArguments接口的class都有一个@@iterator属性,使得arguments对象可遍历。
for…of: 自定义可遍历对象(Iterables)
正如上一节那样,我们可以创建一个自定义的可以通过
for..of
遍历的可遍历对象。这里创建了一个可遍历的
obj
对象,通过[Symbol.iterator]
赋予它一个@@iterator
属性,然后创建一个返回遍历器的方法。记住,遍历器一定要有一个
next()
方法。在next方法里面,我们实现了在for...of遍历过程中会返回的值,这个过程很清晰。
Let’s test this our
obj
against a for..of to see what will happen:耶!成功了!
把Object和普通对象(plain objects)变成可遍历
简单对象是不可遍历的,通过
Object
得到的对象也是不可遍历的。我们可以通过自定义遍历器把@@iterator添加到Object.prototype来实现这个目标。
properties
变量里面包含了通过调用Object.keys()
得到的object的所有属性。在next方法里面,我们只需要返回properties里面的每一个值,并且通过更新作为索引的count变量来获取下一个值。当count达到properties的长度的时候,就把done设为true,遍历就结束了。用Object来测试一下:
成功了!!!
用简单对象来测试一下:
也成功了!! :)
所以我们可以把这个添加到polyfill里面,然后就可以在app里面使用for...of来遍历对象了。
在ES6的类(class)中使用for…of
我们可以用for...of来遍历class的实例中的数据列表。
Profiles类有一个
profile
属性,包含一个用户列表。当我们需要在app中用for...of来展示这个列表的时候,如果这样做:显然是不行的。
为了把
profiles
变成可遍历,请记住以下规则:@@iterator
属性。@@iterator
的方法一定要返回一个遍历器(iterator).iterator
一定要实现next()
方法。我们通过一个熟悉的常量
[Symbol.iterator]
来定义这个@@iterator然后,如果我们这样运行:
我们可以显示 profiles 的属性
Async Iterator
ECMAScript 2018 引入了一个新的语法,可以循环遍历一个 Promise 数组,它就是
for-await-of
和新的 SymbolSymbol.asyncIterator
。iterable 中的
Symbol.asyncIterator
函数需要返回一个返回 Promise 的迭代器。[Symbol.iterator]
和[Symbol.asyncIterator]
的区别在于前者返回的是{ value, done }
而后者返回的是一个Promise
,只不过当 Promise resolve 的时候传入了{ value, done }
。我们上面的那个 f 例子将如下所示:
这个
f
是可异步迭代的,你看它总是返回一个 Promise ,而只有在迭代时 Promise 的 resolve 才返回真正的值。要遍历
f
,我们不能使用for..of
而是要使用新的for-await-of
,它看起来会是这样:我们也可以使用
for-await-of
来循环遍历一个 Promise 数组:Conclusion
在这篇文章中我们深入研究了
for...of
循环,我们首先定义理解什么是for...of
,然后看看什么是可迭代的。for...of
为我们节省了许多复杂性和逻辑,并有助于使我们的代码看起来很清晰易读,如果你还没有尝试过,我认为现在正是时候。