ECMAScript 2015的几个补充,并非新的内置或语法,而是协议。这些协议能够被任何遵循某些约定的对象来实现。 有两个协议:可迭代协议和迭代器协议。git
可迭代协议容许 JavaScript
对象去定义或定制它们的迭代行为, 例如(定义)在一个 for..of
结构中什么值能够被循环(获得)。一些内置类型都是内置的可迭代对象而且有默认的迭代行为, 好比 Array
or Map
, 另外一些类型则不是 (好比Object
) 。数组
Iterator
接口的目的,就是为全部数据结构,提供了一种统一的访问机制,即for...of
循环(详见下文)。当使用for...of
循环遍历某种数据结构时,该循环会自动去寻找 Iterator
接口,调用Symbol.iterator
方法,返回该对象的默认遍历器。bash
ES6
规定,默认的 Iterator
接口部署在数据结构的Symbol.iterator
属性,或者说,一个数据结构只要具备Symbol.iterator
属性,就能够认为是“可迭代的”(iterable
)。Symbol.iterator
属性自己是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器。数据结构
为了变成可迭代对象, 一个对象必须实现(或者它原型链的某个对象)必须有一个名字是 Symbol.iterator
的属性:函数
该迭代器协议定义了一种标准的方式来产生一个有限或无限序列的值。oop
JavaScript
原有的表示“集合”的数据结构,主要是数组(Array
)和对象(Object
),ES6
又添加了Map
和Set
。这样就有了四种数据集合,用户还能够组合使用它们,定义本身的数据结构,好比数组的成员是Map
,Map
的成员是对象。这样就须要一种统一的接口机制,来处理全部不一样的数据结构。ui
迭代器(Iterator
)就是这样一种机制。它是一种接口,为各类不一样的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator
接口,就能够完成遍历操做(即依次处理该数据结构的全部成员)。spa
Iterator
的做用有三个:一是为各类数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员可以按某种次序排列;三是 ES6
创造了一种新的遍历命令for...of
循环,Iterator
接口主要供for...of
消费。指针
Iterator
的遍历过程是这样的。code
建立一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
第一次调用指针对象的next
方法,能够将指针指向数据结构的第一个成员。
第二次调用指针对象的next
方法,指针就指向数据结构的第二个成员。
不断调用指针对象的next
方法,直到它指向数据结构的结束位置。
每一次调用next
方法,都会返回数据结构的当前成员的信息。具体来讲,就是返回一个包含value
和done
两个属性的对象。其中,value
属性是当前成员的值,done
属性是一个布尔值,表示遍历是否结束。
var someString = "hi";
typeof someString[Symbol.iterator]; // "function"
var iterator = someString[Symbol.iterator]();
iterator + ""; // "[object String Iterator]"
iterator.next() // { value: "h", done: false }
iterator.next(); // { value: "i", done: false }
iterator.next(); // { value: undefined, done: true }
复制代码
原生具有 Iterator
接口的数据结构以下。
注意对象是不具有 Iterator
接口的,一个对象若是要具有可被for...of
循环调用的 Iterator
接口,就必须在Symbol.iterator
的属性上部署遍历器生成方法(原型链上的对象具备该方法也可)。
有一些场合会默认调用 Iterator
接口(即Symbol.iterator
方法),除了下文会介绍的for...of
循环,解构赋值, 扩展运算符其实也会调用默认的Iterator
接口。
实际上,这提供了一种简便机制,能够将任何部署了 Iterator
接口的数据结构,转为数组。也就是说,只要某个数据结构部署了 Iterator
接口,就能够对它使用扩展运算符,将其转为数组。
因为数组的遍历会调用遍历器接口,因此任何接受数组做为参数的场合,其实都调用了遍历器接口。下面是一些例子。
for...of 循环是最新添加到 JavaScript 循环系列中的循环。
它结合了其兄弟循环形式 for
循环和 for...in
循环的优点,能够循环任何可迭代(也就是遵照可迭代协议)类型的数据。默认状况下,包含如下数据类型:String、Array、Map
和 Set
,注意不包含 Object
数据类型(即 {}
)。默认状况下,对象不可迭代。
在研究 for...of
循环以前,先快速了解下其余 for
循环,看看它们有哪些不足之处。
for
循环的最大缺点是须要跟踪计数器和退出条件。咱们使用变量 i
做为计数器来跟踪循环并访问数组中的值。咱们还使用 Array.length
来判断循环的退出条件。
虽然 for
循环在循环数组时的确具备优点,可是某些数据结构不是数组,所以并不是始终适合使用 loop 循环。
for...in 循环改善了 for 循环的不足之处,它消除了计数器逻辑和退出条件。可是依然须要使用 index 来访问数组的值.
此外,当你须要向数组中添加额外的方法(或另外一个对象)时,for...in
循环会带来很大的麻烦。由于 for...in
循环循环访问全部可枚举的属性,意味着若是向数组的原型中添加任何其余属性,这些属性也会出如今循环中。这就是为什么在循环访问数组时,不建议使用 for...in
循环。
注意: forEach 循环 是另外一种形式的 JavaScript 循环。可是,forEach() 其实是数组方法,所以只能用在数组中。也没法中止或退出 forEach 循环。若是但愿你的循环中出现这种行为,则须要使用基本的 for 循环。
for...of 循环用于循环访问任何可迭代的数据类型。
const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (const digit of digits) {
console.log(digit);
}
复制代码
能够随时中止或退出 for...of 循环。
const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (const digit of digits) {
if (digit % 2 === 0) {
continue;
}
console.log(digit); //1,3,5,7,9
}
复制代码
不用担忧向对象中添加新的属性。for...of 循环将只循环访问对象中的值。