一、本文解决的问题
ES6
中新增了一个语法for...of
语句,可能很多前端工程师只是看到文档或者博客上都有介绍,它可以用来遍历数组、字符串、Set
集合、Map
等,但是对其中原理不太清楚。本文章就是来答疑解惑的。
二、可迭代对象
如果学过
python
就知道,python
中有可迭代对象及迭代器之分
-
1、可迭代对象有
- 1.
Array
- 2.
String
- 3.
Map
- 4.
Set
- 5.
arguments
- 6.
NodeList
- 1.
-
2、判断一个数据是否具有可迭代能力,只有当数据具有
Symbol.iterator
属性的时候才可以使用for...of
进行迭代console.log(Array.prototype.hasOwnProperty(Symbol.iterator)); console.log(Set.prototype.hasOwnProperty(Symbol.iterator));
-
3、在浏览器控制台打印信息(举例一个数组的)
-
4、从上图看出
Array.prototype[Symbol.iterator]
是一个函数,使用typeof
判断数据类型(如果不可迭代的返回是undefined
)console.log(typeof [][Symbol.iterator]); console.log(typeof {}[Symbol.iterator]); console.log(typeof new Set()[Symbol.iterator]); console.log(typeof ''[Symbol.iterator]);
三、手动模拟一个迭代器
-
1、迭代器的方法
const createIterator = items => { const keys = Object.keys(items); const len = keys.length; let pointer = 0; // 当前的指针位置 return { next() { const done = pointer >= len; const value = !done ? items[keys[pointer++]] : undefined; // 如果当前指针位置小于总长度 return { value, done } } } }
-
2、测试数组
const iterator1 = createIterator([1,2,3,4]); console.log(iterator1.next()); console.log(iterator1.next()); console.log(iterator1.next()); console.log(iterator1.next()); console.log(iterator1.next());
-
3、测试对象
const iterator2 = createIterator({a: 'a', b: 'b', c: 'c'}) console.log(iterator2.next()); console.log(iterator2.next()); console.log(iterator2.next()); console.log(iterator2.next()); console.log(iterator2.next());
四、既然数组等具有可迭代的能力,但是我们直接使用数组.next()会报错的(用python
中的说法,可迭代不代表是一个迭代器,只有迭代器才具有next()
方法)
-
1、错误信息
-
2、通过上面方式判断已经具有可迭代能力的数据,如果要使用
next
函数,必须先将可迭代对象转成换迭代器。 -
3、可迭代对象不一定是迭代器,但是迭代器一定是可迭代对象
五、将不可迭代的数据转换可迭代数据
-
1、尝试在对象使用
for..of
-
2、直接修改对象原型属性
注意一般我们开发中不会直接串改原型,方法将在另外一篇文章分享
Object.prototype[Symbol.iterator] = function() { const self = this; const keys = Object.keys(self); const len = keys.length; let pointer = 0; return { next() { const done = pointer >= len; const value = !done ? self[keys[pointer++]]: undefined; return { done, value } } } } let obj = {name: '哈哈', gender: '男'}; let objItem = obj[Symbol.iterator](); console.log('===', objItem.next()); for (const item of obj) { console.log(item) }
六、关于Generator
函数的介绍
-
1、普通函数只能返回一个值
-
2、
Generator
函数可以返回多个值,也可以接受外界传递的值 -
3、
generator
函数function * generator1() { console.log('开始') yield 1; yield 2; yield 3; console.log('结束') } const iterator3 = generator1(); console.log(typeof iterator3[Symbol.iterator]); console.log(iterator3.next()) for (let item of iterator3) { console.log(item); }
七、关于genertaor
函数传递参数的理解
注意点:只要调用一次
next()
不管里面是否有参数都会yield
一次
-
1、具体代码
function * generator1() { console.log('开始') let aa = yield 1; console.log('aa==>', aa); let bb = yield aa + 2; console.log('bb==>', bb); yield 3; console.log('结束') } const iterator3 = generator1(); console.log(iterator3.next()); // ① console.log(iterator3.next(2)); // ② console.log(iterator3.next(10)); // ③ console.log(iterator3.next()); // ④
-
2、上面具体实现步骤(以调用的步骤为例)
- 1.使用
①
步骤的时候,generator
函数挂起到yield 1
- 2.使用
②
步骤的时候,发送的值直接赋值给aa
,同时是next()
函数会进行下一个yield aa + 2
,当时也仅仅是挂起到yield aa + 2
处 - 3.使用
③
的时候和上面一样的
- 1.使用
-
3、运行结果
开始 { value: 1, done: false } aa==> 2 { value: 4, done: false } bb==> 10 { value: 3, done: false } 结束 { value: undefined, done: true }