因为是关于Iterator的文章,因此前文不得不提起 JavaScript 表示“集合”的数据结构,主要是数组(Array)和 对象(Object),ES6又添加了 Map 和 Set,这样就有了四种数据集合,用户还能够组合使用它们,定义本身的数据结构,这样就须要一种统一的接口机制,来处理全部不一样的数据结构。
---本文摘选阮一峰《ECMAScript 6 标准入门》
说明:遍历器(Iterator)就是这样一种机制。它是一种接口,为各类不一样的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就能够完成遍历操做。
前端
Iterator的做用有三个:
数组
Iterator的遍历过程大概是这样的:
bash
PS:每一次调用 next方法,都会一个包含 value和 done两个属性的对象。数据结构
value 属性是当前成员的值, done 属性是一个布尔值,表示遍历是否结束,来看代码:JS code:
var it = makeIterator(['a', 'b']);
it.next() // { value: "a", done: false }
it.next() // { value: "b", done: false }
it.next() // { value: undefined, done: true }
function makeIterator(array) {
var nextIndex = 0;
return {
next: function() {
return nextIndex < array.length ? {value: array[nextIndex++], done: false} : {value: undefined, done: true};
}
};
}复制代码
说明:makeIterator 是遍历器生成函数,做用就是返回一个遍历器对象。对数组 ['a', 'b'] 执行这个函数,就会返回该数组的遍历器对象 ( 指针对象 ) it,指针对象的 next 方法,用来移动指针,next 方法返回一个对象,对象具备 value函数
说明:Iterator接口的目的:为全部数据结构提供统一的访问机制( for...of循环 ) ,当使用 for...of 循环遍历某种数据结构时,该循环会自动去寻找Iterator接口。ui
ES6规定,数据结构只要具备 Symbol.iterator 属性,就是可遍历的this
Symbol.iterator 属性自己是一个函数,是当前数据结构默认的遍历器生成函数,执行这个函数,就会返回一个遍历器。spa
PS:Symbol.iterator 是一个表达式,返回prototype
JS code:
const obj = {
[Symbol.iterator]: function () {
return {
next: function () {
return {
value: 1,
done: true
};
}
};
}
};
复制代码
说明:对象 obj 是可遍历的,由于有 Symbol.iterator指针
在ES6中,有三类数据结构原生具有Iterator接口:数组、某些相似数组的对象、Set和Map结构,来看代码:
JS code:
let arr = ['a', 'b', 'c'];
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: undefined, done: true }
复制代码
说明:变量arr是一个数组,原生具备遍历器接口,部署在 arr 的 Symbol.iterator 属性上面,因此,调用这个属性,就获得遍历器对象。
上面提到三类数据结构原生具有Iterator接口,有可能会问为何没有咱们常见的对象Object,那如今为你们解答,对象之因此没有默认部署Iterator接口,是由于对象的哪一个属性先遍历,哪一个属性后遍历是不肯定的,须要开发者手动指定,因此一个对象若是要有可被
JS code:
class RangeIterator {
constructor(start, stop) {
this.value = start;
this.stop = stop;
} [Symbol.iterator]() { return this; }
next() {
var value = this.value;
if (value < this.stop) {
this.value++;
return {done: false, value: value};
} else {
return {done: true, value: undefined};
}
}
}
function range(start, stop) {
return new RangeIterator(start, stop);
}
for (var value of range(0, 3)) {
console.log(value);
}复制代码
说明:Symbol.iterator 属性对应一个函数,执行后返回当前对象的遍历器对象。
下面是另外一个为对象添加Iterator接口的例子,来看代码:
JS code:
let obj = {
data: [ 'hello', 'world' ],
[Symbol.iterator]() {
const self = this;
let index = 0;
return {
next() {
if (index < self.data.length) {
return {
value: self.data[index++],
done: false
};
} else {
return { value: undefined, done: true };
}
}
};
}
};
复制代码
PS:对于相似数组的对象(存在数值键名和length属性)部署Iterator接口,有一个简便方法,就是 Symbol.iterator 方法直接引用数组的Iterator接口,来看代码:
JS code:
NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
// 或者
NodeList.prototype[Symbol.iterator] = [][Symbol.iterator];
[...document.querySelectorAll('div')] // 能够执行了
复制代码
若是是相似数组的对象调用数组的 Symbol.iterator 方法呢?来看代码
JS code:
let iterable = {
0: 'a',
1: 'b',
2: 'c',
length: 3,
[Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let item of iterable) {
console.log(item); // 'a', 'b', 'c'
}
复制代码
注意:普通对象部署数组的 Symbol.iterator 方法,并没有效果。
有一些场合会默认调用Iterator接口(即
JS code:
let set = new Set().add('a').add('b').add('c');
let [x,y] = set;
// x='a'; y='b'
let [first, ...rest] = set;
// first='a'; rest=['b','c'];
复制代码
JS code:
// 例一
var str = 'hello';
[...str] // ['h','e','l','l','o']
// 例二
let arr = ['b', 'c'];
['a', ...arr, 'd']
// ['a', 'b', 'c', 'd']
复制代码
说明:提供了一种简便机制,能够将任何部署了Iterator接口的数据结构 转为数组,即只要某个数据结构部署了Iterator接口,就能够对它使用扩展运算符,将其转为数组。
JS code:
let generator = function* () {
yield 1;
yield* [2,3,4];
yield 5;
};
var 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 }
复制代码
字符串是一个相似数组的对象,也原生具备Iterator接口,来看代码:
JS code:
var someString = "hi";
typeof someString[Symbol.iterator]
// "function"
var iterator = someString[Symbol.iterator]();
iterator.next() // { value: "h", done: false }
iterator.next() // { value: "i", done: false }
iterator.next() // { value: undefined, done: true }
复制代码
说明:调用
固然还能够覆盖原生的
JS code:
var str = new String("hi");
[...str] // ["h", "i"]
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"
复制代码
说明:字符串 str 的
Symbol.iterator 方法的最简单实现,仍是使用Generator函数,来看代码:
JS code:
var myIterable = {};
myIterable[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
};
[...myIterable] // [1, 2, 3]
// 或者采用如下简单写法
let obj = {
* [Symbol.iterator]() {
yield 'hello';
yield 'world';
}
};
for (let x of obj) {
console.log(x);
};
// hello
// world复制代码
说明:Symbol.iterator 方法几乎不用部署代码,仅用 yield 命令给出每一步的返回值便可。
说明:遍历器对象除了具备
return 适用场景:若是 for...of 循环提早退出(一般是由于出错,或有 break 语句 或 continue 语句),就会调用
JS code:
function readLinesSync(file) {
return {
next() {
if (file.isAtEndOfFile()) {
file.close();
return { done: true };
}
},
return() {
file.close();
return { done: true };
},
};
}
复制代码
说明:函数 readLinesSync 接受一个文件对象做为参数,返回一个遍历器对象,其中除了
throw 方法主要是配合Generator函数使用,通常的遍历器对象用不到这个方法。
好了,关于 Iterator 混子前端就总结到这里,老规矩,欢迎点赞和纠错!
最后,祝你们工做愉快!
( 心里os:明天终于周五了 )