简介
Iterator称为遍历器,是ES6为不同数据结构遍历所新增的统一访问接口。
作用:
- 为任何部署了Iterator接口的数据结构提供统一的访问机制。
- 使得数据结构的成员能够按照某种次序排列。
- 为新的遍历方式for…of提供基础。
一个合法的Iterator接口都会具有一个next()函数,在遍历的过程中,依次调用next()函数,返回一个带有value和done属性的对象。value值表示当前遍历到的值,done值表示迭代是否结束,true表示迭代完成,Iterator执行结束;false表示迭代未完成,继续执行next()函数,进入下一轮遍历中,直到done值为true。
原生具备Iterator接口的数据结构
只有部署了Iterator接口的数据结构才能使用for…of遍历:
- Array
- Map
- Set
- String
- 函数的arguments对象
- NodeList对象
- Generator()函数的返回值 ```javascript // 对象默认不能使用for…of循环 const obj = { name: ‘kingx’, age: 11 }; for (let key of obj) { console.log(key); // Uncaught TypeError: obj is not iterable }
// 借助Object.keys()、Object.values()、Object.entries()间接实现对象使用for…of for (let key of Object.keys(obj)) { console.log(key); // name, age } for (let value of Object.values(obj)) { console.log(value); // kingx, 11 } for (let [key, value] of Object.entries(obj)) { console.log(key, ‘:’, value); } // name : ‘kingx’, // age : 11,
// 数组:返回数组中的每个值 const arr = [‘one’, ‘two’]; for (let key of arr) { console.log(key); // one, two }
// Set:返回Set中的每个值。 let set = new Set([‘one’, ‘two’, ‘three’]); for (let key of set) { console.log(key); // one, two, three }
// Map:将键和对应的值组合成一个个数组进行返回。 let map = new Map([[‘name’, ‘kingx’], [‘age’, 12], [‘address’, ‘beijing’]]); for (let prop of map) { console.log(prop); } // [ ‘name’, ‘kingx’ ] // [ ‘age’, 12 ] // [ ‘address’, ‘beijing’ ]
// NodeList类数组对象
这是第一个段落
这是第二个段落
这是第三个段落
// arguments类数组对象 function foo() { for (let arg of arguments) { console.log(arg); // name,age,address } } foo(‘name’, ‘age’, ‘address’);
// Generator()函数的返回值是一个部署了Iterator接口的对象,刚好可以使用for…of循环进行遍历,并且不需要手动调用next()函数,遍历的结果就是yield表达式的返回值。 function* testGenerator() { yield ‘hello’; yield ‘world’; }
const t = testGenerator(); for (let key of t) { console.log(key); // 先后输出”hello””world” }
**自定义实现可以使用for...of循环的数据结构:**<br />Iterator接口是部署在Symbol.iterator属性上的,它是一个函数,所以只需要对特定的数据结构加上Symbol.iterator属性即可。Symbol.iterator属性对应的函数中一定要返回一个带有next()函数的对象,在next()函数中需要返回带有value和done属性的对象,以此来满足Iterator的执行过程。```javascript// 为对象类型的数据添加Iterator接口,使得它也可以使用for...of循环function Person(name, age) {this.name = namethis.age = age}// 在原型中添加[Symbol.iterator]属性Person.prototype[Symbol.iterator] = function () {let index = 0// 通过Object.keys()函数获取实例自身的所有属性let propArr = Object.keys(this)return {next: function () {// 如果当前索引值index小于propArr数组的长度,则value值为propArr数组中对应索引位置的值,done值为false并且index值会递增if (index < propArr.length) {return {value: propArr[index++],done: false}} else {// 当count值等于属性的长度时,遍历结束,设置done值为truereturn {value: undefined,done: true}}}}}const person = new Person('kingx', 12)for (let key of person) {console.log(key, ':', person[key])}// name : kingx// age : 12
// 借助Generatorfunction* propGenerator() {let propArr = Object.keys(this);for (let prop of propArr) {// 通过yield控制每轮循环的返回值为由属性名和属性值构成的数组yield [prop, this[prop]];}}let obj = {name: 'kingx',age: 12};// 为obj对象添加Symbol.iterator属性obj[Symbol.iterator] = propGenerator;for (let [key, value] of obj) {console.log(key, ':', value);}// name : kingx// age : 12
数组来模拟Iterator接口的实现
// 将数组传进去,返回一个带有next()函数的遍历器对象function makeIterator(array) {let index = 0return {next: function () {if (index < array.length) {// 如果当前索引值index小于数组的长度,则value值为数组中对应索引位置的值,done值为false并且index值会递增return {value: array[index++],done: false}} else {// 直到index的值等于数组的长度才结束遍历,此时value值为undefined,done值为true。return {value: undefined,done: true}}}}}const arr = ['one', 'two']const iter = makeIterator(arr)iter.next() // {value: "one", done: false}iter.next() // {value: "two", done: false}iter.next() // {value: undefined, done: true}
for..of、for..in、forEach()对比
forEach()缺点:forEach()循环的主要问题在于无法跳出循环,不支持break和continue关键字,如果使用了break或continue关键字则会抛出异常,使用return关键字会跳过当前循环,但仍会执行后续的循环。
const arr = ['one', 'two', 'three']arr.forEach(function (item, index) {if (index === 1) {// 这里如果使用break和continue关键字,会抛出异常,使用return关键字会跳过当前循环return item}console.log(item) // one three})
for…in缺点:主要是为遍历对象设计的,对数组遍历并不友好。返回的键是字符串表示的数组的索引,如“0”“1”“2”,并不是数组项的值。同时通过手动给数组实例添加的属性,同样会被遍历出来。
const arr = ['one', 'two', 'three']arr.name = 'myArr'for (let key in arr) {console.log(key, typeof key)// 0 string// 1 string// 2 string// name string}
for…of优点:返回的是数组每项的值,而且给数组实例新增的属性并不会被遍历出来。而且可以使用break、continue和return等关键字。
const arr = ['one', 'two', 'three']arr.name = 'myArr'for (let key of arr) {if (key === 'three') {break;}console.log(key) // one two}
