JavaScript的迭代器(Iterator)介绍
迭代器是数据结构遍历的一种机制(或者是什么我也不太懂的行业术语),为数据结构定义了统一的遍历规则。 迭代器(Iterator)主要是提供for...of
使用,或者说,数据结构只有实现了迭代器(Iterator)接口才能使用for...of
进行遍历数据,当一种数据结构没有实现迭代器(Iterator)接口时,去使用for...of
操作,就会被抛出异常Uncaught TypeError: xxx is not iterable
。
JavaScript的迭代器(Iterator)的接口规范和操作过程:
- 迭代器被调用时,返回一个指针对象,指针对象中必须包含一个
next()
的方法,每次调用next()
方法,都会返回一个代表当前成员的信息对象,具有value
和done
两个属性。value
可为任意数据类型,done
则是一个布尔类型,当调用next
的方法时返回的对象中的done
属性为false
时,表示还可以继续进行遍历,当done
属性为true
时,表示遍历结束(没有的东西遍历了)了。 - 迭代器(Iterator)对象的可选属性
return()
方法和throw()
方法,也就是说当我们要自己去实现迭代器(Iterator)的时候,迭代器里必须要有next()
方法,而return()
方法和throw()
是否要实现是可选的。而自己实现迭代器(Iterator)时,不管是next()
、return()
还是throw()
方法必须有返回值并且是对象,否则进行遍历的时候会抛出异常Uncaught TypeError: Iterator result xxx is not an object
。 - 自己实现迭代器,只要给数据结构或者对象添加
[Symbol.iterator]
属性即可,其值必须是一个函数,返回一个只针对象,需遵循第2点的规范。为什么必须是[Symbol.iterator]
的属性名,因为JavaScript的定义中,寻找遍历器时就是用这个字段,这也是一个标准规范。
下面是自己实现迭代器的演示代码:
const obj = {a: "age 18", b: 2}; // 实现迭代器 obj[Symbol.iterator] = function () { const keys = Object.keys(obj); let keyIndex = 0; return { next() { if (keys.length === 0 || keyIndex >= keys.length) { return { value: undefined, done: true } } const key = keys[keyIndex], value = [key, obj[key]]; keyIndex += 1; return { value, done: false } } }; } // 使用for...of进行遍历 for (let [key, value] of obj) { console.log(`${key}--${value}`) } // a--age 18 // b--2
返回参数简写,当返回正常值的时候,done
字段可以省略,当循环结束的时候,value
可以省略。但是注意不能两个都不写,不然会死循环,而且必须要有条件结束的操作,不然也会死循环,就像递归一样,一定要有条件结束的操作
Number.prototype[Symbol.iterator] = function () { let that = +this, i = that < 0 ? that : 0; that = that < 0 ? 0 : that; return { next() { if (i <= that) { const value = i; i += 1; return { value }; } return { done: true }; } }; }; for (const item of 20) { console.log(item); } // 数组的扩展运算符也是调用迭代器的哦 console.log([...5]);// [0, 1, 2, 3, 4, 5]
迭代器的可选参数return()
和throw()
return()
方法是在遍历中断的时候会调用,如使用了break
关键字中断或者抛出了异常都会调用这个方法。return()
方法必须有返回参数并且要求是object
类型的数据,否则就会抛出异常Uncaught TypeError: Iterator result undefined is not an object
,至于object
里内容要求是什么,我测了一下,毫无影响,返回空对象也没问题。
const obj = { size: 5, [Symbol.iterator]() { return { // 这里用箭头函数为的是可以直接使用this next: () => { if (this.size >= 0) { const value = this.size; this.size -= 1; return { value }; } return { done: true }; }, return() { console.log("中断了"); return { done: true }; // 返回以下内容照样不会有问题,但是最好不这么操作,因为代码具有语义化才能更好的阅读 // return {}; // return new Date(); } }; } }; for (const item of obj) { if (item < 3) { break;// 中断了 } console.log(item); }
throw()
方法在迭代器中基本用不到,而是配合Generator
使用,这里就不做过多的叙述。
原生具备 Iterator 接口的数据结构如下:
- Array
- Map
- Set
- String
- TypedArray
- 函数的 arguments 对象
- NodeList 对象
for...of循环与for...in循环
上面已经详细的说明了迭代器(Iterator)主要是提供for...of
循环使用,所以for...of
循环时调用的是迭代器(Iterator),而循环的值是由实现的迭代器(Iterator)而定,而for...in
循环是循环键值。
const arr = ["a", "b", "c"]; // 原生的数组迭代器(Iterator)的实现遍历时返回的是每一个元素 for (const item of arr) { console.log(item); // a // b // c } // for...in 返回的是key,这里是数组,key就是索引 for (const key in arr) { console.log(key); // 0 // 1 // 2 }
还有一个更直观的区别。for...of
只是根据迭代器(Iterator)实现的内容返回结果,所以就不会遍历不在范围的东西,而for...in
会把所有的键遍历出来。
const arr = ["a", "b", "c"]; arr.testValue = 1; for (const item of arr) { console.log(item); // a // b // c } for (const item in arr) { console.log(item); // 0 // 1 // 2 // testValue }
到此这篇关于JavaScript中Iterator迭代器接口与循环的文章就介绍到这了,更多相关JavaScript Iterator 内容请搜索阿兔在线工具以前的文章或继续浏览下面的相关文章希望大家以后多多支持阿兔在线工具!