一、什么是for-of循环
对于如下一个数组,遍历其中的值方法有哪些?
var arr = ['a', 'b', 'c'];
首先想到的可能就是如下这种,这也是js最原始的遍历方法,和java的语法一样。
var arr = ['a', 'b', 'c'];
for (var i=0;i
如果你了解ES5,还可以写出for-in的方法。
var arr = ['a', 'b', 'c'];
for(var i in arr){
console.log(arr[i]);
}
今天为大家再介绍一种for-of的遍历方法
var arr = ['a', 'b', 'c'];
for(var ch of arr){
console.log(ch);
}
在这个例子中,大家注意到使用for-of时,ch不是数组的索引值,所示其中的value值。
那有了for-in,为什么还要设计for-of呢,或者说for-of有什么独特的地方。继续实例:
let arr = "abc";
for( var char of arr){
console.log(char);
}
此时,可以将a,b,c输出,但是for-in是无能为力的。其中for-of不仅能对数组和字符串进行遍历。还可以适用以下的集合:
Map
Set
NodeList
其中Map,Set是ES6新增的特性,现在不了解也没关系,我们稍后单独讲。NodeList是DOM对象集合,有兴趣的可能查阅资料了解,不在这里展开讨论。
二、迭代器(Iterator)实现原理
再看一个普通的字面量对象例子:
var obj={0:"rrr",1:"444"};
for(var value of obj){
console.log(value);
}
运行后发现报错,表明这个字面量对象是不可遍历的。那能否像Array,String等对象一样支持for-of遍历呢?答案是可以的,Array,String等对象之所以可遍历,是因为在他们的原型上,实现了迭代器(Iterator)的接口。
那如何实现迭代器(Iterator)接口呢,只需要在这个对象中实现[Symbol.iterator]属性方法,[Symbol.iterator]这个属性名ka看起来怪怪的,Symbol是ES6表示唯一性的标识符,标准委员会是为了避免命名的冲突,所以才这样命名,大家作为一个唯一的字符串理解即可,不去深究。下面我们来实现[Symbol.iterator]属性。如下:
var obj={
0:"rrr",
1:"444",
//实现[Symbol.iterator]属性方法
[Symbol.iterator]:function(){
const self = this;
let index=0;
return {
next:function(){//实现next
if(index<2){
return {//遍历中
value:self[index++],
done: false//表示遍历没有结束,done设置为fasle
}
}else{
return{//遍历结束
value:undefined,//结束后,返回undefined
done: true//done设置为true,表示遍历结束
}
}
}
}
}
};
for(var value of obj){
console.log(value);//"rrr","444"
}
此时正确的遍历了obj,打印了obj的属性的值。再来看下[Symbol.iterator]属性函数体,返回了next方法,next方法中遍历了此obj的属性对象,并返回字面量对象{value:xxx,done:xxx}这种结构对象,在遍历过程中,value返回对应属性值,且done设置为false;当遍历结束后,value返回undefined,且done设置为true。
var it = obj[Symbol.iterator]();
console.log(it.next());//{value: "rrr", done: false}
console.log(it.next());//{value: "444", done: false}
console.log(it.next());//{value: undefined, done: true}
这就是for-of的原理,每次遍历都会调用该对象的[Symbol.iterator]属性的next方法,当返回{value: undefined, done: true}后,表示遍历结束。
所以讲到这,各位道友是否就明白了,任何对象要变成可遍历的对象,只需要实现[Symbol.iterator]属性方法,定义其中的next方法即可。
三、break,continue
学过java的道友们,对break,continue应该再熟悉不过了,意义我就不再阐述。for-of对于for-in的优点就是它能支持这些语句。我们接着往下看。
let arr = "abc";
for( var char of arr){
console.log(char);//"a"
break;
}
仅输出了“a”就跳出了循环。有兴趣的道友可以再实验下continue,看看效果。
好了,我们将上面obj的实例再修改下,使其支持这些语句
var obj={
0:"rrr",
1:"444",
//实现[Symbol.iterator]属性方法
[Symbol.iterator]:function(){
const self = this;
let index=0;
return {
next:function(){//实现next
if(index<2){
return {//遍历中
value:self[index++],
done: false//表示遍历没有结束,done设置为fasle
}
}else{
return{//遍历结束
value:undefined,//结束后,返回undefined
done: true//done设置为true,表示遍历结束
}
}
},
return:function() {//增加return,支持continue,break
return { done: true };
}
}
}
};
for(var value of obj){
console.log(value);//"rrr"
break ;
}
此时仅输出了"rrr"就跳出了循环,在执行break时,内部调用了return的方法,返回了{done:false},continue也是如此。
四、总结
主要学些了for-of循环,并通过这个循环讲述了迭代器(Iterator)的实现原理。for-of的用处还是很强大的,可以和我们下一章节要介绍的Generator对象很好的结合。
上一篇:ES6系列教程第一篇--Promise详解 下一篇:ES6系列教程第三篇--Generator 详解