简介
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 = name
this.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值为true
return {
value: undefined,
done: true
}
}
}
}
}
const person = new Person('kingx', 12)
for (let key of person) {
console.log(key, ':', person[key])
}
// name : kingx
// age : 12
// 借助Generator
function* 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 = 0
return {
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
}