1.Iterator 的遍历过程
说明:
- 遍历对象本质上就是一个指针对象。
- 第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
- 第二次调用指针对象的next方法,可以将指针指向第二个成员。
- 依次进行。
2.默认Iterator接口
说明: Iterator接口的目的,就是为所有数据接口,提供了一种统一的访问机制,即for...of循环。一种数据结构只要部署了Iterator接口,我们称为这种数据结构时可遍历的。 ES6规定,默认的Iterator接口在数据结构Symbol.iterator属性,只要一个数据结构只要具有Symbol.iterator属性,就可以认为是可遍历的。
2.1例子
说明:Symbol.iterator属性本身是一个函数,就是当前数据结构默认的遍历器生成函数,执行这个函数,就会返回一个遍历器。
const obj = {
// 具有Symbol.iterator属性。
[Symbol.iterator]: function () {
// 返回一个遍历器对象。
return {
// 对象的根本特征就是具有next方法。调用next方法就会返回成员的信息
next: function () {
return {
content: 1,
done: true,
};
},
};
},
};
console.log(obj[Symbol.iterator]().next()); //{content: 1, done: true}
2.2原生具有Iterator接口
说明:Array,Map,Set,String,NodeList对象,函数的arguments对象。
2.3数组
let arr = ["张三", "李四", "王二"];
let iterator = arr[Symbol.iterator]();
console.log(iterator.next()); //{value: '张三', done: false}
console.log(iterator.next()); //{value: '李四', done: false}
console.log(iterator.next()); //{value: '王二', done: false}
console.log(iterator.next()); //{value: undefined, done: true}
注意:上诉代码中,变量arr时一个数组,具有Iterator遍历器接口。对于原生部署Iterator接口的数据结构,不用自己写遍历器生成的函数,for...of循环会自动遍历他们。
2.4类似数组
说明:对于类似数组的对象(存在数值键名和length属性),部署Iterator接口,有一个简便方法,就是Symbol.iterator方法直接引用数组的Iterator接口。
const obj1 = {
0: "张三",
1: "李四",
2: "王二",
length: 3,
[Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let i of obj1) {
console.log(i);
//张三
// 李四
// 王二
}
注意:普通对象部署数组的Symbol.Iterator方法,并无效果。
3.调用Iterator接口情景
3.1.解构赋值
说明:数组和Set结构进行结构赋值时,会默认调用Symbol.iterator方法
let set = new Set()
set.add("李四").add("张三").add("王二")
let [first, second] = set
console.log(first); //李四
console.log(second);//张三
3.2.扩展运算符
const str = "GOOD"
console.log(...str); //G O O D
// 总结:任何部署了Iterator结构的数据的结构,都可以转成数组
3.3.yield*
说明:yield*后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口
const generator = function* () {
yield 1;
yield* ["张三", "李四"]
yield 5
}
const iterator1 = generator()
console.log(iterator1.next());//{value: 1, done: false}
console.log(iterator1.next());//{value: '张三', done: false}
console.log(iterator1.next());//{value: '李四', done: false}
console.log(iterator1.next());//{value: 5, done: false}
console.log(iterator1.next());//{value: undefined, done: true}
3.4.字符串的Iterator接口
说明:字符串是一个类似数组的对象,也原生具有Iterator接口。
let str1 = "FORE"
console.log(typeof str1[Symbol.iterator]); //funcion
// typeof可以检测除了null的基本数据结构,只能检测funtcion的引用数据类型
const iterator2 = str1[Symbol.iterator]()
console.log(iterator2.next()); //{value: 'F', done: false}
console.log(iterator2.next());//{value: 'F', done: false}
console.log(iterator2.next());//{value: 'R', done: false}
console.log(iterator2.next());//{value: 'E', done: false}
console.log(iterator2.next());//{value: undefined, done: true}
3.5.Iterator接口与Generaotor函数
const iterator3 = {
[Symbol.iterator]: function* () {
yield 1
yield 2
yield 3
}
}
console.log(...iterator3); //1 2 3
// 简写形式
const iterator4 = {
*[Symbol.iterator]() {
yield 1
yield 2
}
}
console.log(...iterator4); //1 2
3.6. 遍历器对象的return(),throw()
说明: 如果自己部署的话,那么return()和throw()是可选的,return主要对象在完成遍历时候,需要清理或释放资源,就可以部署return方法。
let obj3 = {
0: "张三",
length: 1,
[Symbol.iterator]: function () {
return {
next: function () {
return {
content: "张三",
done: false
}
},
}
}
}
let iterator5 = obj3[Symbol.iterator]()
console.log(iterator5.next()); //张三
3.7.for...of
说明:for...of作为遍历所有数据结构统一的方法,循环调用遍历器接口可以遍历Set和Map结构,某些类似数组的对象。
const arr1 = ["张三", "李四", "王二"]
for (let i of arr1) {
console.log(i);
//张三
// 李四
// 王二
}
3.7.1.for...of替代了forEach()
arr1.forEach((item, index, arr) => {
console.log(item);
console.log(index);
console.log(arr);
})
3.7.2.Js中原有的for...in循环
const arr2 = ["张三", "李四", "王二"]
for (let i in arr2) {
console.log(i);
// 0
// 1
// 2
}
3.7.3.Set和Map结构
const set1=new Set(["张三","李四","王二"])
for(let i of set1){
console.log(i);
// 张三
// 李四
// 王二
}
const map1=new Map([["name","李四"],["age",18]])
for(let i of map1){
console.log(i);
// ['name', '李四']
// ['age', 18]
}
3.7.4.类似数组的对象
let str2="SPAY"
for(let i of str2){
console.log(i);
//S
//P
//A
//Y
}
function fun(){
for(let i of arguments){
console.log(i);
// 1
// 2
// 3
}
}
fun(1,2,3)
3.7.5对象
说明:只有部署了遍历器的函数的对象才可以用for...of遍历.
4.其他遍历语法的比较
说明:
- for函数过于复杂
- forEach()方法不能配合break,continue,return使用
- for...in循环主要是为遍历对象而设计的,不适用于遍历数组。
- for...of循环相比上面几种做法,有一些显著的优点,不同于forEach方法,它可以与break、continue和return配合使用,提供了遍历所有数据结构的统一操作接口。