let arr = [1,2,3,4,5,6];
for(let i=0;i<arr.length;i++){
.....
}
缺陷:代码不够简洁
let arr = [1,2,3,4,5,6];
arr.forEach(function(value,index){
.....
})
缺陷:无法中断停止整个循环
let arr = [1,2,3,4,5,6];
for(let i in arr){
.....
}
缺陷:for…in循环常用于对象的循环遍历,不推荐用于遍历数组。 如果用于数组遍历就得注意,例如上述代码,for…in循环中的i是字符串类型,而不是数字类型
for...of
是一种用于遍历数据结构
(可遍历:数组、对象、字符串、set和map结构等具有Iterator接口的数据结构)的方法
let arr = [1,2,3,4,5,6];
for(let val of arr){
console.log(val); // 1 2 3 4 5 6
}
优势:写法比for循环简洁很多
let arr = [1,2,3,4,5,6];
for(let v of arr){
if(v === 3){
//终止掉整个循环
break;
}
console.log(v); // 1 2
}
优势:可以用break终止整个循环
let arr = [1,2,3,4,5,6];
for(let v of arr){
if(v === 3){
//跳过当前循环,并继续后面的循环
continue;
}
console.log(v); // 1 2 4 5 6
}
优势:可以用continue跳出当前循环,并继续后面的循环
let arr = [1,2,3,4,5,6];
//值
for(let v of arr.values()){
console.log(v); // 1 2 3 4 5 6
}
//索引
for(let i of arr.keys()){
console.log(i); // 0 1 2 3 4 5
}
//索引 + 值
for(let [i,v] of arr.entries()){
console.log('索引'+i,'值'+v);
// 索引0 值1
// 索引1 值2
// 索引2 值3
// 索引3 值4
// 索引4 值5
// 索引5 值6
}
优势:for…of循环可以直接获取到键、值
for…of还支持字符串、类数组、set和map解构的遍历,这里就偷个小懒不一一举例了
注意:
for…of不可以遍历Object对象,不信的话可以自己写写试试,它会报错的。
Why???为什么数组,Set和Map结构又可以支持for…of的遍历呢?
要想被for…of正常遍历,都需要实现一个遍历器Iterator,而数组、Set和Map结构内置就有遍历器Iterator(又可以叫迭代器),在它们的原型中有Symbol.iterator方法,而Object对象并没有实现这个接口所以不能用for…of遍历
//数组
Array.prototype[Symbol.iterator]; // function values(){...}
//字符串
String.prototype[Symbol.iterator]; // function [Symbol.iterator](){...}
//Set结构
Set.prototype[Symbol.iterator]; // function values(){...}
//Map结构
Mao.prototype[Symbol.iterator]; // function entries(){...}
//Object对象
Object.prototype[Symbol.iterator]; // undefined
注
:Symbol.iterator
是Symbol 对象
的 iterator 属性
,是一个特殊的Symbol值,因此,当它作为prototype对象属性名的时候,获取它的时候需要使用[ ]
的形式: prototype[Symbol.iterator]
,不能使用点
形式获取
:prototype.Symbol.iterator
。原因是点
形式会把后面的值
当作是字符串类型
处理,而不是Symbol类型。
拥有[Symbol.iterator]()
方法的数据结构,可以被for…of遍历(可遍历对象)
当
可遍历对象
被for...of遍历
的时候,[Symbol.iterator]()
就会被调用
,返回一个iterator对象
。
每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。
//数组(可遍历对象)
let arr = ['a','b','c','d'];
//调用数组的Symbol.iterator()方法
let iter = arr[Symbol.iterator]();
iter.next(); // {value:'a',done:false}
iter.next(); // {value:'b',done:false}
iter.next(); // {value:'c',done:false}
iter.next(); // {value:'d',done:false}
iter.next(); // {value:undefined,done:true}
先调用可遍历对象的[Symbol.iterator]()
方法,得到一个iterator遍历器对象,然后就在遍历器上不断调用next()
,直到done
的值为true
的时候,就代表遍历完成
结束了
一个
对象
如果要具备可被for...of
循环调用的Iterator
接口,就必须在Symbol.iterator
的属性上部署/添加遍历器生成方法
//定义一个Object对象
let obj = {
0:'a',
1:'b',
2:'c',
length:3,
//添加[Symbol.iterator]方法
[Symbol.iterator]:function(){
let _this = this;
let index = 0;
return {
next:()=>{
let value = _this[index];
let done = (index >= _this.length);
index++;
return { value,done }
}
}
}
};
//试一下for...of
for(let v of obj){
console.log(v);
// a
// b
// c
}
这个例子就是说我们可以创建一个可遍历的对象,并且自定义它的遍历行为。或者说可以通过添加[Symbol.iterator]()
方法,把一个不可遍历的Object对象
,变成可遍历的对象
。
类似数组的对象,本来就具有遍历接口,可以直接遍历。将它的遍历接口改成数组的Symbol.iterator属性
let obj = {
0:'a',
1:'b',
2:'c',
length:3,
[Symbol.iterator]:Array.prototype[Symbol.iterator]
};
for(let v of obj){
console.log(v);
// a
// b
// c
}
注:
普通对象部署数组的Symbol.iterator方法,并无效果。
let obj = {
a:'a',
b:'b',
c:'c',
length:3,
[Symbol.iterator]:Array.prototype[Symbol.iterator]
};
for(let v of obj){
console.log(v);
// undefined
// undefined
// undefined
}
除了
for...of循环
,还有几个别场合会默认调用Symbol.iterator方法
对
数组
和Set 结构
进行解构赋值
时,会默认调用Symbol.iterator方法。
let set = new Set().add('a').add('b').add('c');
let [x,y] = set; // x='a',y='b'
let [start,...end] = set; // start='a',end=['b','c']
扩展运算符(...)
也会调用默认的 Symbol.iterator方法。
let str = 'hello';
[...str] //['h','e','l','l','o']
let arr = ['a','b','c'];
[...arr,'d'] //['a','b','c','d']
yield*
后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口
let generator = function* (){
yield 1;
yield* [2,3,4];
yield 5;
}
let iterator = generator();
iterator.next() // { value: 1, done: false }
iterator.next() // { value: 2, done: false }
iterator.next() // { value: 3, done: false }
iterator.next() // { value: 4, done: false }
iterator.next() // { value: 5, done: false }
iterator.next() // { value: undefined, done: true }
由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其实都调用了遍历器接口。
new Map([['a',1],['b',2]])
)