一、本文解决的问题
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 }