迭代器(Iterator)
ES5完成迭代器
迭代器是什么?碰到这类新的观点,莫慌张。
迭代器是一种特别对象,每个迭代器对象都有一个next(),该要领返回一个对象,包括value和done属性。
ES5完成迭代器的代码以下:
//完成一个返回迭代器对象的函数,注重该函数不是迭代器,返回效果才叫做迭代器。
function createIterator(items) {
var i = 0;
return {
next() {
var done = (i >= items.length); // 推断i是不是小于遍历的对象长度。
var value = !done ? items[i++] : undefined; //假如done为false,设置value为当前遍历的值。
return {
done,
value
}
}
}
}
const a = createIterator([1, 2, 3]);
//该要领返回的终究是一个对象,包括value、done属性。
console.log(a.next()); //{value: 1, done: false}
console.log(a.next()); //{value: 2, done: false}
console.log(a.next()); //{value: 3, done: false}
console.log(a.next()); //{value: undefined, done: true}
天生器(Generator)
天生器是函数:用来返回迭代器。
这个观点有2个症结点,一个是函数、一个是返回迭代器。这个函数不是上面ES5中建立迭代器的函数,而是ES6中特有的,一个带有*(星号)的函数,同时你也须要运用到yield。
//天生器函数,ES6内部完成了迭代器功用,你要做的只是运用yield来迭代输出。
function *createIterator() {
yield 1;
yield 2;
yield 3;
}
const a = createIterator();
console.log(a.next()); //{value: 1, done: false}
console.log(a.next()); //{value: 2, done: false}
console.log(a.next()); //{value: 3, done: false}
console.log(a.next()); //{value: undefined, done: true}
天生器的yield症结字有个奇异的功用,就是当你实行一次next(),那末只会实行一个yield背面的内容,然后语句停止运转。
在for轮回中运用迭代器
纵然你是在for轮回中运用yield症结字,也会停息轮回。
function *createIterator(items) {
for(let i = 0; i < items.length; i++) {
yield items[i]
}
}
const a = createIterator([1, 2, 3]);
console.log(a.next()); //{value: 1, done: false}
yield运用限定
yield只能够在天生器函数内部运用,假如在非天生器函数内部运用,则会报错。
function *createIterator(items) {
//你应当在这里运用yield
items.map((value, key) => {
yield value //语法毛病,在map的回调函数内里运用了yield
})
}
const a = createIterator([1, 2, 3]);
console.log(a.next()); //无输出
天生器函数表达式
函数表达式很简单,就是下面这类写法,也叫匿名函数,不必纠结。
const createIterator = function *() {
yield 1;
yield 2;
}
const a = createIterator();
console.log(a.next());
在对象中增加天生器函数
一个对象长如许:
const obj = {}
我们能够在obj中增加一个天生器,也就是增加一个带星号的要领:
const obj = {
a: 1,
*createIterator() {
yield this.a
}
}
const a = obj.createIterator();
console.log(a.next()); //{value: 1, done: false}
可迭代对象和for of轮回
再次朗读一遍,迭代器是对象,天生器是返回迭代器的函数。
通常经由过程天生器天生的迭代器,都是能够迭代的对象(可迭代对象具有Symbol.iterator属性),也就是能够经由过程for of将value遍历出来。
function *createIterator() {
yield 1;
yield 2;
yield 3;
}
const a = createIterator();
for(let value of a) {
console.log(value)
}
// 1 2 3
上面的例子通知我们天生器函数返回的迭代器是一个能够迭代的对象。实在我们这里要研讨的是Symbol.iterator的用法。
function *createIterator() {
yield 1;
yield 2;
yield 3;
}
const a = createIterator(); //a是一个迭代器
const s = a[Symbol.iterator]();//运用Symbol.iterator接见迭代器
console.log(s.next()) //{value: 1, done: false}
Symbol.iterator还能够用来检测一个对象是不是可迭代:
typeof obj[Symbol.iterator] === "function"
建立可迭代对象
在ES6中,数组、Set、Map、字符串都是可迭代对象。
默许状况下定义的对象(object)是不可迭代的,然则能够经由过程Symbol.iterator建立迭代器。
const obj = {
items: []
}
obj.items.push(1);//如许子虽然向数组增加了新元素,然则obj不可迭代
for (let x of obj) {
console.log(x) // _iterator[Symbol.iterator] is not a function
}
//接下来给obj增加一个天生器,使obj成为一个能够迭代的对象。
const obj = {
items: [],
*[Symbol.iterator]() {
for (let item of this.items) {
yield item;
}
}
}
obj.items.push(1)
//如今能够经由过程for of迭代obj了。
for (let x of obj) {
console.log(x)
}
内建迭代器
上面提到了,数组、Set、Map都是可迭代对象,即它们内部完成了迭代器,而且供应了3种迭代器函数挪用。
1、entries() 返回迭代器:返回键值对
//数组
const arr = ['a', 'b', 'c'];
for(let v of arr.entries()) {
console.log(v)
}
// [0, 'a'] [1, 'b'] [2, 'c']
//Set
const arr = new Set(['a', 'b', 'c']);
for(let v of arr.entries()) {
console.log(v)
}
// ['a', 'a'] ['b', 'b'] ['c', 'c']
//Map
const arr = new Map();
arr.set('a', 'a');
arr.set('b', 'b');
for(let v of arr.entries()) {
console.log(v)
}
// ['a', 'a'] ['b', 'b']
2、values() 返回迭代器:返回键值对的value
//数组
const arr = ['a', 'b', 'c'];
for(let v of arr.values()) {
console.log(v)
}
//'a' 'b' 'c'
//Set
const arr = new Set(['a', 'b', 'c']);
for(let v of arr.values()) {
console.log(v)
}
// 'a' 'b' 'c'
//Map
const arr = new Map();
arr.set('a', 'a');
arr.set('b', 'b');
for(let v of arr.values()) {
console.log(v)
}
// 'a' 'b'
3、keys() 返回迭代器:返回键值对的key
//数组
const arr = ['a', 'b', 'c'];
for(let v of arr.keys()) {
console.log(v)
}
// 0 1 2
//Set
const arr = new Set(['a', 'b', 'c']);
for(let v of arr.keys()) {
console.log(v)
}
// 'a' 'b' 'c'
//Map
const arr = new Map();
arr.set('a', 'a');
arr.set('b', 'b');
for(let v of arr.keys()) {
console.log(v)
}
// 'a' 'b'
虽然上面列举了3种内建的迭代器要领,然则差别鸠合的范例另有自身默许的迭代器,在for of中,数组和Set的默许迭代器是values(),Map的默许迭代器是entries()。
for of轮回解构
对象自身不支持迭代,然则我们能够自身增加一个天生器,返回一个key,value的迭代器,然后运用for of轮回解构key和value。
const obj = {
a: 1,
b: 2,
*[Symbol.iterator]() {
for(let i in obj) {
yield [i, obj[i]]
}
}
}
for(let [key, value] of obj) {
console.log(key, value)
}
// 'a' 1, 'b' 2
字符串迭代器
const str = 'abc';
for(let v of str) {
console.log(v)
}
// 'a' 'b' 'c'
NodeList迭代器
迭代器真是无处不在啊,dom节点的迭代器你应当已用过了。
const divs = document.getElementByTagName('div');
for(let d of divs) {
console.log(d)
}
睁开运算符和迭代器
const a = [1, 2, 3];
const b = [4, 5, 6];
const c = [...a, ...b]
console.log(c) // [1, 2, 3, 4, 5, 6]
高等迭代器功用
你说什么?上面讲了一堆空话都是基本功用?另有高等功用没讲?
高等功用不庞杂,就是传参、抛出非常、天生器返回语句、托付天生器。
1、传参
天生器内里有2个yield,当实行第一个next()的时刻,返回value为1,然后给第二个next()传入参数10,通报的参数会替换掉上一个next()的yield返回值。鄙人面的例子中就是first。
function *createIterator() {
let first = yield 1;
yield first + 2;
}
let i = createIterator();
console.log(i.next()); // {value: 1, done: false}
console.log(i.next(10)); // {value: 12, done: false}
2、在迭代器中抛出毛病
function *createIterator() {
let first = yield 1;
yield first + 2;
}
let i = createIterator();
console.log(i.next()); // {value: 1, done: false}
console.log(i.throw(new Error('error'))); // error
console.log(i.next()); //不再实行
3、天生器返回语句
天生器中增加return示意退出操纵。
function *createIterator() {
let first = yield 1;
return;
yield first + 2;
}
let i = createIterator();
console.log(i.next()); // {value: 1, done: false}
console.log(i.next()); // {value: undefined, done: true}
4、托付天生器
天生器嵌套天生器
function *aIterator() {
yield 1;
}
function *bIterator() {
yield 2;
}
function *cIterator() {
yield *aIterator()
yield *bIterator()
}
let i = cIterator();
console.log(i.next()); // {value: 1, done: false}
console.log(i.next()); // {value: 2, done: false}
异步使命实行器
ES6之前,我们运用异步的操纵体式格局是挪用函数并实行回调函数。
书上举的例子挺好的,在nodejs中,有一个读取文件的操纵,运用的就是回调函数的体式格局。
var fs = require("fs");
fs.readFile("xx.json", function(err, contents) {
//在回调函数中做一些事变
})
那末使命实行器是什么呢?
使命实行器是一个函数,用来轮回实行天生器,由于我们晓得天生器须要实行N次next()要领,才运转完,所以我们须要一个自动使命实行器帮我们做这些事变,这就是使命实行器的作用。
下面我们编写一个异步使命实行器。
//taskDef是一个天生器函数,run是异步使命实行器
function run(taskDef) {
let task = taskDef(); //挪用天生器
let result = task.next(); //实行天生器的第一个next(),返回result
function step() {
if(!result.done) {
//假如done为false,则继承实行next(),而且轮回step,直到done为true退出。
result = task.next(result.value);
step();
}
}
step(); //最先实行step()
}
测试一下我们编写的run要领,我们不再须要console.log N个next了,由于run实行器已帮我们做了轮回实行操纵:
run(function *() {
let value = yield 1;
value = yield value + 20;
console.log(value) // 21
})
总结
本章讲了3个观点,迭代器、天生器、使命实行器。
迭代器是一个对象。
天生器是一个函数,它终究返回迭代器。
使命实行器一个函数(或许也叫天生器的回调函数),帮我们自动实行天生器的内部运算,终究返回迭代器。
不晓得看到这里,你邃晓3者的区分和用法没?