文章目录
Iterator 的概念
遍历器(Iterator
就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作
(即依次处理该数据结构的所有成员)。
-
Iterator 的作用有三个
- 一、 是为各种数据结构,提供一个统一的、简便的访问接口;
- 二 、是使得数据结构的成员能够按某种次序排列;
-
三 、是 ES6 创造了一种新的遍历命令
for...of
循环,Iterator 接口主要供for…of消费。
next方法
返回值
function upper(array){
let index = 0
return{
next(){
return index < array.length ? {value:array[index++], done:false}: {value:undefined, done:true}
// return index < array.length ? {value:array[index++]}: {done:true}
// 简便的写法(下面会有介绍原因)
}
}
}
let maker = new upper([1, 2, 3])
console.log( maker.next()) // value: 1 done: false
console.log( maker.next()) // value: 2 done: false
console.log( maker.next()) // value: 3 done: false
console.log( maker.next()) // value: undefined done: true
console.log( maker.next()) // value: undefined done: true
//每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回
//一个包含`value`和`done`两个属性的对象。其中,`value属性是当前成员的值`,
//`done属性是一个布尔值,表示遍历是否结束`。
- 指针对象的
next方法
,用来移动指针。开始时,指针指向数组的开始位置
。然后,每次调用next方法,指针就会指向数组的下一个成员。第一次调用,指向 1 ;第二次调用,指向 2 。 next
方法返回一个对象,表示当前数据成员的信息。这个对象具有value
和done
两个属性,value属性返回当前位置的成员,done属性是一个布尔值,表示遍历是否结束,即是否还有必要再一次调用next方法。- 对于遍历器对象来说,
done: false
和value: undefined
属性都是可以省略的,所以我们在代码编写时可以忽略这两个属性的设置
默认的 Iterator接口
- ES6 规定,默认的
Iterator 接口
部署在数据结构的Symbol.iterator
属性,或者说,一个数据结构只要具有Symbol.iterator
属性,就可以认为是“可遍历的”(iterable)。 Symbol.iterator
属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器。至于属性名Symbol.iterator
,它是一个表达式
,返回Symbol对象的iterator属性,这是一个预定义好的、类型为 Symbol 的特殊值,所以要放在方括号内。
// 手动部署遍历器生成方法
// 为对象添加 Iterator 接口
const obj = {
[Symbol.iterator] : function () {
return {
next: function () {
return {
value: 1,
done: true
};
}
};
}
};
obj[Symbol.iterator]().next() // {value: 1, done:true}
- 对于
类似数组
的对象(存在数值键名和length属性
),部署Iterator
接口,有一个简便方法,就是Symbol.iterato
r方法直接引用数组
的 Iterator 接口。
// NodeList 为类数组对象
NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
// 或者
NodeList.prototype[Symbol.iterator] = [][Symbol.iterator];
- 但是对于非类数组对象,上面的方法是无效的。
let iterable = {
a: 'a',
b: 'b',
length: 3,
[Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let value of iterable) {
console.log(value); // undefined, undefined
}
调用 Iterator 接口的场合
-
(1)解构赋值
-
对数组和 Set 结构进行解构赋值时,会默认调用
Symbol.iterator
方法。
let newarrray = [1, 2, 3, 4]
let [start, ...end] = newarrray
let newset = new Set([1, 2, 2, 3])
let [start1, ...end1] = newset
// 上面的两种方法都会默认调用 Symbol.iterator方法,即遍历 newarray 和 newset
-
(2)扩展运算符
- 扩展运算符(…)也会调用默认的 Iterator 接口。
// 例一
var str = 'hello';
[...str] // ['h','e','l','l','o']
// 例二
let arr = ['b', 'c'];
['a', ...arr, 'd']
// ['a', 'b', 'c', 'd']
-
(3)其他场合
-
由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其实都调用了遍历器接口。下面是一些例子。
-
for...of
Array.from()
Map()
,Set()
,WeakMap()
,WeakSet()
Promise.all()
Promise.race()
上面这些的参数都可以接收数组作为参数。
字符串的 Iterator 接口
- 下面是一种比较特殊的情况
var str = new String("hi");
str[Symbol.iterator] = function() {
return {
next: function() {
if (this._first) {
this._first = false;
return { value: "bye", done: false };
} else {
return { done: true };
}
},
_first: true
};
};
[...str] // ["bye"]
str // "hi"
// 由于扩展运算符(...)使用时会默认调用 iterator 接口,而 iterator 部署
// 在 Symbol.iterator 上,所以此时我们会执行 Symbol.iterator 内的方法
// 在手动更改了他的属性之后,可以看到有明显不同的效果
Iterator 接口与 Generator 函数
let myIterable = {
[Symbol.iterator]: function* () {
yield 1;
yield 2;
yield 3;
}
}
[...myIterable] // [1, 2, 3]
// Generator函数会在下一篇博客详细的介绍
for…of 循环
-
一个数据结构只要部署了
Symbol.iterator
属性,就被视为具有 iterator 接口,就可以用for...of
循环遍历它的成员。也就是说,for...of
循环内部调用的是数据结构的Symbol.iterator
方法。 -
for…of循环可以使用的范围包括
数组
、Set 和 Map 结构
、某些类似数组的对象
(比如arguments对象、DOM NodeList 对象)、Generator 对象
,以及字符串
。 -
数组
let newerarrray = [1, 2, 3, 4]
for(let value of newerarrray){
console.log(value)
// 1 2 3 4
}
--------------------------------------------------------------------
//for...of循环调用遍历器接口,数组的遍历器接口只返回具有数字索引的属性
let newerarrray = [1, 2, 3, 4]
newerarrray.foo = 'hello'
for(let value of newerarrray){
console.log(value)
// [1, 2, 3, 4]
}
// 此时的数组结构 [1, 2, 3, 4, foo:'hello'],由于 foo不是数字索引,所以不返回'hello'
--------------------------------------------------------------------
//for...of循环读取键值。如果要通过for...of循环,获取数组的索引,可以借助
//数组实例的entries方法和keys方法
newerarrray = ['a', 'b', 'c']
for(let value of newerarrray.entries()){
console.log(value)
// [0,'a'], [1, 'b'], [2, 'c']
// 输出同时具有索引和值得数组
}
for(let value of newerarrray.keys()){
console.log(value)
// 0 1 2
// 输出每个值得索引
}
- Set 和 Map 结构
// Set 结构遍历时,返回的是一个值
let newset = new Set([1, 2, 2, 3])
for (let value of newset){
console.log(value)
// 1, 2, 3
}
// Map 结构遍历时,返回的是一个数组,该数组的两个成员分别为当前 Map 成员
// 的键名和键值。
let map = new Map().set('a', 1).set('b', 2);
for (let pair of map) {
console.log(pair);
}
// ['a', 1]
// ['b', 2]
- 类似数组的对象
// 字符串
let str = "hello";
for (let s of str) {
console.log(s); // h e l l o
}
// DOM NodeList对象
let paras = document.querySelectorAll("p");
for (let p of paras) {
p.classList.add("test");
// 为不同的 p标签 添加新的类名,如已经存在,取消添加
}
// arguments对象
function printArgs() {
for (let x of arguments) {
console.log(x);
}
}
printArgs('a', 'b');
// 'a'
// 'b'
-------------------------------------------------------------------
//当遇到类数组对象不能够用 for...of遍历时,我们可以将其转化为数组
```javascript
let arrayLike = { length: 2, 0: 'a', 1: 'b' };
for (let x of Array.from(arrayLike)) {
......
}
- 对象
//对于普通的对象,for...of结构不能直接使用,会报错,必须部署了 Iterator 接
//口后才能使用
let stu = {
name : 'Jack',
age : 18
}
for (let value of stu){} // Error
// 部署Iterator的方法 => 添加 Symbol.iterator属性