不难看出 for…of 默认得到值, 而 for…in 只能得到索引。当然数组的 for…of 只返回数字索引的属性, 而 for…in 没有限制:
var arr = [“red”, “green”, “blue”];
arr.name = “color”;
for(let v of arr){
console.log(v); //依次输出 “red”, “green”, “blue”
}
for(let i in arr){
console.log(arr[i]); //依次输出 “red”, “green”, “blue”, “color”
}
- Set
默认 for…of 遍历器遍历值
var set = new Set([“red”, “green”, “blue”]);
for(let v of set){ //相当于 for(let i in arr.values())
console.log(v); //依次输出 “red”, “green”, “blue”
}
for(let [key, value] of set.entries()){
console.log(key + ": " + value); //依次输出 “red: red”, “green: green”, “blue: blue”
}
for(let key of set.keys()){
console.log(key); //依次输出 “red”, “green”, “blue”
}
- map
默认 for…of 遍历器遍历键值对
var map = new Map();
map.set(“red”, “#ff0000”);
map.set(“green”, “#00ff00”);
map.set(“blue”, “#0000ff”);
for(let [key, value] of map){ //相当于 for(let i in arr.entries())
console.log(key + ": " + value); //依次输出 “red: #ff0000”, “green: #00ff00”, “blue: #0000ff”
}
for(let value of map.values()){
console.log(value); //次输出 “#ff0000”, “#00ff00”, “#0000ff”
}
for(let key of map.keys()){
console.log(key); //次输出 “red”, “green”, “blue”
}
- 字符串
for…of可以很好的处理区分32位 Unicode 字符串
var str = “Hello”;
for(let v of str){
console.log(v); //依次输出 “H”, “e”, “l”, “l”, “o”
}
- 类数组对象
// DOM NodeList
var lis = document.getElementById(“li”);
for(let li of lis){
console.log(li.innerHTML); //遍历每个节点
}
//arguments
function fun(){
for(let arg of arguments){
console.log(arg); //遍历每个参数
}
}
不是所有类数组对象都有 iterator, 如果没有, 可以先用Array.from()
进行转换:
var o = {0: “red”, 1: “green”, 2: “blue”, length: 3};
var o_arr = Array.from(o);
for(let v of o_arr){
console.log(v); //依次输出 “red”, “green”, “blue”
}
技巧1: 添加以下代码, 使 fo 《大厂前端面试题解析+Web核心总结学习笔记+企业项目实战源码+最新高清讲解视频》无偿开源 徽信搜索公众号【编程进阶路】 r…of 可以遍历 jquery 对象:
$.fn[Symbol.iterator] = [][Symbol.iterator];
技巧2: 利用 Generator 重新包装对象:
function* entries(obj){
for(let key of Object.keys(obj)){
yield [key, obj[key]];
}
}
var obj = {
red: “#ff0000”,
green: “#00ff00”,
blue: “#0000ff”
};
for(let [key, value] of entries(obj)){
console.log(${key}: ${value}
); //依次输出 “red: #ff0000”, “green: #00ff00”, “blue: #0000ff”
}
几种遍历方法的比较
-
for 循环: 书写比较麻烦
-
forEach方法: 无法终止遍历
-
for…in: 仅遍历索引, 使用不便捷; 会遍历原型链上的属性, 不安全; 会遍历非数字索引的数组属性;
-
for…of:
iterator 与 [Symbol.iterator]
iterator 遍历过程是这样的:
1. 创建一个指针对象, 指向当前数据结构的起始位置。即遍历器的本质就是一个指针。
2. 调用一次指针的 next 方法, 指针指向第一数据成员。之后每次调用 next 方法都会将之后向后移动一个数据。
3. 知道遍历结束。
我们实现一个数组的遍历器试试:
var arr = [1, 3, 6, 5, 2];
var it = makeIterator(arr);
console.log(it.next()); //Object {value: 1, done: false}
console.log(it.next()); //Object {value: 3, done: false}
console.log(it.next()); //Object {value: 6, done: false}
console.log(it.next()); //Object {value: 5, done: false}
console.log(it.next()); //Object {value: 2, done: false}
console.log(it.next()); //Object {value: undefined, done: true}
function makeIterator(arr){
var nextIndex = 0;
return {
next: function(){
return nextIndex < arr.length ?
{value: arr[nextIndex++], done: false} :
{value: undefined, done: true}
}
};
}
由这个例子我们可以看出以下几点:
-
迭代器具有 next() 方法, 用来获取下一元素
-
next() 方法具有返回值, 返回一个对象, 对象 value 属性代表下一个值, done 属性表示是否遍历是否结束
-
如果一个数据结构本身不具备遍历器, 或者自带的遍历器不符合使用要求, 请按此例格式自定义一个遍历器。
其实一个 id 生成器就很类似一个遍历器:
function idGen(){
var id = 0;
return {
next: function(){ return id++; }
};
}
var id = idGen();
console.log(id.next()); //0
console.log(id.next()); //1
console.log(id.next()); //2
//…
对于大多数数据结构, 我们不需要再像这样写遍历器函数了。因为他们已经有遍历器函数[Symbol.iterator]
, 比如Array.prototype[Symbol.iterator]
是数组结构的默认遍历器。
下面定义一个不完整(仅包含add()方法)的链表结构的实例:
function Node(value){
this.value = value;
this.next = null;
}
function LinkedList(LLName){
this.head = new Node(LLName);
this.tail = this.head;
}
var proto = {
add: function(value){
var newNode = new Node(value);
this.tail = this.tail.next = newNode;
return this;
}
}
LinkedList.prototype = proto;
LinkedList.prototype.constructor = LinkedList;
LinkedList.prototype[Symbol.iterator] = function(){