谈谈遍历与迭代协议

谈谈遍历与迭代

其实遍历与迭代是意义很近的一组词,但为啥要要把它们都列出来呢?先要从遍历说起:

遍历在js中的形式

The for loop

for循环是最普遍的遍历方式, 下面是一个简单的栗子;

1
2
3
4
5
const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (let i = 0; i < digits.length; i++) {
console.log(digits[i]);
}

for循环的不足之处就是必须维护计数器i与退出条件digits.length, 对刚学习编程的人来说,
这可能有点困惑,不能一眼看出这是在做啥;

还有一点是for循环很适合数组类型,但是js不只是只有数组类型,因此for循环不是一个所有类型的解决方案.

The for…in loop

for...in循环优化了for循环的不足之处-需要维护计数器的变化与退出条件;

1
2
3
4
5
const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (const index in digits) {
console.log(digits[index]);
}

并且这对对象也是适用的,但是这个遍历方式也有一些问题:

正如上面code中,遍历的是计数器,当需要每个value值的时候,需要重新取值digits[index];

并且for…in会把所有的可枚举属性遍历出来,如增加到原型链上的方法,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Array.prototype.decimalfy = function() {
for (let i = 0; i < this.length; i++) {
this[i] = this[i].toFixed(2);
}
}
const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (const index in digits) {
console.log(digits[index]);
};
// 输出
// 0 1 2 3 4 5 6 7 8 9
/*
function() {
 for (let i = 0; i < this.length; i++) {
  this[i] = this[i].toFixed(2);
 }
}
*/

这也是for…in没有在js程序员中普遍流行的原因;

注意:forEach方法也是js遍历数组的一种方法,forEach实际上是一个数组方法,因而一般
适用在数组上,并且没有方法跳出循环;

for…of

直接看一个栗子:

1
2
3
4
for(let v of 'hello'){
console.log(v);
}
// h e l l o

for…of是针对集合(注意:非set而是collections),而不是所有的对象,适用范围是iterable对象;

什么是iterable对象?

迭代

程序员是一群不停折腾的物种,为了更加方便的遍历,在ES6引入了迭代协议;迭代协议有两种:

  • iterable协议
  • iterator协议

由于这次主题是讲遍历与迭代,所以只涉及第一种协议;

定义

什么是iterable, ECMA官方文档是如下定义的:

Property Value
[Symbol.iterator] A zero arguments function that returns an object, conforming to the iterator protocol.

也就是说只要具有[Symbol.iterator]属性,并且其值是一个0参数函数,这个函数执行返回的是一个符合iterator协议的对象,
符合上述条件的对象就是一个可迭代对象;

1
2
3
var a = []
typeof a[Symbol.iterator] // function
a[Symbol.iterator]() // Array Iterator {}

范围

目前JS内置的可迭代对象有String, Array, Map, Set, TypedArray, arguments等;需要注意的是普通的obj是不能迭代的;

几个用法

遍历:

1
2
3
4
5
var someString = 'hi';
for(let v of something) {
console.log(v);
}
// h i

适用于元素展开:

1
2
3
var set1 = new Set([0, 1, 2]);
var set2 = new Set([2, 3, 4, 5]);
var arr = [...set1, ...set2]; // [0, 1, 2, 2, 3, 4, 5]

转换数组

1
Array.from(new Set(['a', 1, 'b', 2]))

转换成生成器

1
2
3
4
5
6
var arr = [1,2,3];
var iter = arr[Symbol.iterator]();
iter.next(); // {value: 1, done: false}
iter.next(); // {value: 2, done: false}
iter.next(); // {value: 3, done: false}
iter.next(); // {value: undefined, done: true}