一、本文解决的问题
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) } -
3、一般直接修改对象本身
obj1[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 } } } }
六、关于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 }