Es6的迭代器深入理解
今天学习了Es6中的迭代器的知识,在这里做一个总结。
一、 概念理解:
迭代器:就是可以将一个有限并且有序的数据类型,进行有序输出的函数。
可迭代对象:就是具有可迭代接口的实例化对象。
迭代器工厂函数:例如Symbol.iterator就是迭代器工厂函数。
二、可迭代的数据类型
<script>
let arr = [1, 2, 3, 4, 5, 6];
let str = 'woshidongli';
let obj = { a: 3, b: 5 };
for(let x of arr){
console.log(x);
}
for(let x of str){
console.log(x);
}
for(let x of obj){
console.log(x);
} //迭代器_1.html:26 Uncaught TypeError: obj is not iterable
</script>
由此可见数组有可以进行迭代,字符串也可以,但是Object不可以迭代。
原因:因为迭代对象必须是有限的,并且有次序的,但是当我们查看这几种数据类型输出的时候。
这里数组类型有序号,但是对象类型序号。
但这并不是根本的原因:根本的原因是:是否有可迭代接口(Symbol.iterator)当我们使用Object.getPrototypeOf()来访问实例对象原型时。
数组和字符串中
对象中
对象中并没有Symbol.iterator,所以可以知道不可以迭代。
拓展
<script>
let str = 'abcd';
let arr = ['a','b','c','d'];
let map = new Map().set("a",1).set('b',2);
let set = new Set().add("a").add('b').add('c');
let nodelist = document.querySelectorAll("div");
console.log(str[Symbol.iterator]);
console.log(arr[Symbol.iterator]);
console.log(map[Symbol.iterator]);
console.log(set[Symbol.iterator]);
console.log(nodelist[Symbol.iterator]);
</script>
由此可以知道,Map,Set,NodeList,Array,String,等数据类型都是可以迭代的。
三、迭代器内部实现
迭代实现原理就是,当可迭代实例对象进行循环遍历的时候,就会调用接口执行其中的函数。
下面我们手写数组的迭代器。
<script>
let arr = [1,2,3,4,5,6,7];
arr[Symbol.iterator]=function(){
let index = 0;
return {
next(){
if (index<arr.length){
return{
value:arr[index++],
done:false
}
}
else{
return {
value:undefined,
done:true
}
}
}
}
}
let itertor = arr[Symbol.iterator]();
console.log(itertor.next());
console.log(itertor.next());
console.log(itertor.next());
console.log(itertor.next());
console.log(itertor.next());
console.log(itertor.next());
console.log(itertor.next());
console.log(itertor.next());
console.log(itertor.next());
</script>
最终结果
Object对象没有迭代对象
我们下面手写对象的迭代器
<script>
let obj = {
a:1,
b:2,
c:3,
d:4,
[Symbol.iterator](){
let index = 0 ;
let map = new Map([["a",1],["b",2],["c",3],["d",4]]);
return {
next(){
if(index<map.size){
return {
value:[...map][index++],
done:false,
}
}
else{
return{
value:undefined,
done:true
}
}
}
}
}
}
for (const x of obj){
console.log(x);
}
</script>
最终结果:
解析:这里我们采用了Map作为中间转化,因为Map是可迭代的数据类型。我们也可以使用Object.keys()
Object.values()
Object.entries()
这三个来对对象进行迭代。
还有一点比较重要:当一个实例化对象可以进行迭代,他的[Symbol.iterator]也可以进行迭代,也就是说实例化对象的[Symbol.iterator]也存在iterator接口。
四、迭代器协议
<script>
let arr = ['arr','bat'];
let text_1 = arr[Symbol.iterator]();
let text_2 = arr[Symbol.iterator]();
console.log(text_1.next());
console.log(text_2.next());
console.log(text_1.next());
console.log(text_2.next());
</script>
解析:当声明不同的迭代器的时候,这几个迭代器之间不互相干扰。
<script>
let arr = [1,2,4,5,6];
let iterator = arr[Symbol.iterator]();
console.log(iterator.next());
arr.splice(1,0,3);
console.log(iterator.next());
//{value: 1, done: false}
//{value: 3, done: false}
</script>
解析:迭代器使用的是游标记录迭代过程,当迭代过程中,实例化对象改变的时候,迭代器也会发生改变。
五、提前终止迭代器
我们在使用迭代器进行迭代的时候,可以在满足某一个条件的时候,就会提前终止迭代器。起始在[Symbol.iterator]也存在一个函数,return()函数,就是满足终止条件的时候,执行return()函数。
例子
<script>
let arr = [1,2,3,4,5,6];
let iterator = arr[Symbol.iterator]();
iterator.return = function(){
console.log("结束了");
return {
value:undefined,
done:true
}
}
for (const x of iterator){
console.log(x);
break;
}
</script>
这里设置一个return函数,当执行结束的时候,就自动执行return中的。
但是数组有一个特点:不可关闭的,就是当数组break退出的时候,下一次,还是会继续在原来的游标位置开始迭代。
<script>
let arr = [1,2,3,4,5,6];
let iterator = arr[Symbol.iterator]();
iterator.return = function(){
console.log("结束了");
return {
value:undefined,
done:true
}
}
for (const x of iterator){
console.log(x);
break;
}
for (const x of iterator){
console.log(x);
}
</script>
还有一点比较容易混淆:【Symbol.iterator】和【Symbol.iterator】(),
【Symbol.iterator】是工厂函数,【Symbol.iterator】()是迭代器,【Symbol.iterator】下面没有iterator接口,而【Symbol.iterator】()中有iterator接口,所有【Symbol.iterator】()可以进行迭代。
这些就是我对于ES6中的迭代器的理解,如果存在任何错误,请指正。