此篇文章主要讲解了Iterator(迭代器)-Generator(生成器),在实际开发中用的不是很多,但是对于理解async/await有一定的帮助,同时对React中的redux库的使用,有很大的帮助
大家在阅读文章的时候,有写错的地方,还请多多指正!
//在js中,我们知道数组可以遍历
let arr = [1,2,3]
//是因为我们在创建数组的时候,js内部帮我们用了迭代器,生成了可迭代对象
let arr = [1, 2, 3, 4];
//此时iteratorArr就是arr的迭代器
let iteratorArr = {
index: 0,
next() {
//没有遍历完的时候,done为false,value为arr[i]
if (this.index < arr.length) {
return { done: false, value: arr[this.index++] };
} else {
//遍历完成只会,done为false,value可以省略
return { done: true };
}
},
};
console.log(iteratorArr.next());
console.log(iteratorArr.next());
console.log(iteratorArr.next());
console.log(iteratorArr.next());
console.log(iteratorArr.next());
let arr = [1, 2, 3, 4];
function createIteratorArr(arrName) {
index = 0;
//返回一个对象,对象中包含next方法,所以可以链式调用
return {
next() {
if (index < arrName.length) {
return { done: false, value: arrName[index++] };
} else {
//遍历完成只会,done为false,value可以省略
return { done: true };
}
},
};
}
let arr1 = createIteratorArr(arr);
console.log(arr1.next());
console.log(arr1.next());
console.log(arr1.next());
刚刚我们了解了迭代器的原理,那么什么是可迭代对象呢?
可迭代对象与迭代器是不同的概念
当一个对象实现了iterable protocol协议的时候,就是一个可迭代对象
这个对象要求必须事项@@iterator方法,在代码中我们使用Symbol.iterator访问该属性
//创建一个迭代器,迭代obj中的num属性
let obj = {num:[1,2,3,]}
let iteratorArr = {
index: 0,
next() {
//没有遍历完的时候,done为false,value为arr[i]
if (this.index < obj.num.length) {
return { done: false, value: obj.num[this.index++] };
} else {
//遍历完成只会,done为false,value可以省略
return { done: true };
}
},
};
let obj = {
arr: [1, 2, 3, 4],
//这是迭代器函数固定的命名[Symbol.iterator]
[Symbol.iterator]() {
//迭代器本身是一个对象,对象中包含next方法
return {
index: 0,
next() {
//没有遍历完的时候,done为false,value为arr[i]
if (this.index < obj.arr.length) {
return { done: false, value: obj.arr[this.index++] };
} else {
//遍历完成只会,done为false,value可以省略
return { done: true };
}
},
};
},
};
let objIter = obj[Symbol.iterator]();
console.log(objIter.next());
console.log(objIter.next());
console.log(objIter.next());
for (const item of obj) {
console.log(item);
}
//1 2 3 4
let arr = [1, 2, 3, 4];
//获取迭代器
let arrIter = arr[Symbol.iterator]();
console.log(arrIter.next());
console.log(arrIter.next());
console.log(arrIter.next());
/**{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false } */
obj
制作成了可迭代对象,那么我们能否对其进行优化obj.arr
,如果对象使用了别的名字,那么就需要进行更改
[Symbol.iterator]
函数是被obj调用,所以在[Symbol.iterator]
函数的内部this就是obj[Symbol.iterator]
函数调用的,所以next内部的this指向就是[Symbol.iterator]
函数[Symbol.iterator]
传递到next中;将next变成箭头函数//将 ``[Symbol.iterator]``传递到next中;
let obj = {
arr: [1, 2, 3, 4],
//这是迭代器函数固定的命名[Symbol.iterator]
[Symbol.iterator]() {
const _this = this;
//迭代器本身是一个对象,对象中包含next方法
return {
index: 0,
next() {
//没有遍历完的时候,done为false,value为arr[i]
if (this.index < _this.arr.length) {
return { done: false, value: _this.arr[this.index++] };
} else {
//遍历完成只会,done为false,value可以省略
return { done: true };
}
},
};
},
};
for (const item of obj) {
console.log(item);
}
//将next变成箭头函数
let obj = {
arr: [1, 2, 3, 4],
//这是迭代器函数固定的命名[Symbol.iterator]
[Symbol.iterator]() {
//迭代器本身是一个对象,对象中包含next方法
let index = 0;
return {
next: () => {
//没有遍历完的时候,done为false,value为arr[i]
if (index < this.arr.length) {
return { done: false, value: this.arr[index++] };
} else {
//遍历完成只会,done为false,value可以省略
return { done: true };
}
},
};
},
};
for (const item of obj) {
console.log(item);
}
let object = {
name: "zhangcheng",
age: 18,
[Symbol.iterator]() {
//可以遍历keys单独获取键
let keys = Object.keys(this);
//可以遍历values单独获取值
let values = Object.values(this);
let index = 0;
//使用entries可以获取键值对
let keyValues = Object.entries(this);
const iterator = {
next: () => {
if (index < keys.length) {
return { done: false, value: keyValues[index++] };
} else {
return { done: true };
}
},
};
return iterator;
},
};
for (const iterator of object) {
console.log(iterator);
}
String Array Map Set argument对象 NodeList集合
都是可迭代对象
//展开语法这里需要特别说明一下
let obj = {a:100}
let obj2 = {...obj}//这种是浏览器做了特殊处理,在ES8左右新增的,可以在创建对象字面量的时候使用展开运算符
function foo(a,b){
}
foo(...obj)//在这里运用展开运算符的时候就会报错
new Map new WeakMap new Set new WeakSet
Promise.all Promise.race Array.from
创建一个类,由这个类创建的所有对象都是可以迭代的
class Person {
constructor(name, age, friends) {
this.name = name;
this.age = age;
this.friends = friends;
}
//在实例对象上创建实例方法
[Symbol.iterator]() {
let index = 0;
//对对象的key进行迭代
let keys = Object.keys(this);
const iterator = {
next: () => {
if (index < keys.length) {
return { done: false, value: keys[index++] };
} else {
return { done: true };
}
},
};
return iterator;
}
}
let p = new Person("zhangcheng", 18, "jame");
for (const iterator of p) {
console.log(iterator);
}
当使用for循环遍历的时候,其内部使用了break、return、throw等中断了遍历,可以在迭代器中使用return函数,监听此类操作
class Person {
constructor(name, age, friends) {
this.name = name;
this.age = age;
this.friends = friends;
}
//在实例对象上创建实例方法
[Symbol.iterator]() {
let index = 0;
//对对象的key进行迭代
let keys = Object.keys(this);
const iterator = {
next: () => {
if (index < keys.length) {
return { done: false, value: keys[index++] };
} else {
return { done: true };
}
},
//终止的时候,默认执行return函数
return: () => {
console.log("终止了");
//函数内部需要返回一个对象,否则会报错
return { done: true };
},
};
return iterator;
}
}
let p = new Person("zhangcheng", 18, "jame");
for (const iterator of p) {
break;
}
学习生成器,对于我们理解async/await的理解会有帮助;同时对于React中的redux库的使用,也会有很大的帮助
生成器是ES6新增的一种函数控制、使用的方案,可以让我们灵活的控制函数什么时候继续执行、暂停执行
生成器是特殊的迭代器
function* foo() {
console.log(111);
console.log(222);
yield;
console.log(333);
yield;
console.log(444);
console.log(555);
console.log(666);
console.log(777);
}
const generator = foo();
generator.next(); //111 222
generator.next(); //111 222 333
generator.next(); //全部打印
yield value
function* foo() {
console.log(111);
console.log(222);
yield "bbb";
console.log(333);
yield "ccc";
console.log(444);
console.log(555);
console.log(666);
console.log(777);
}
const generator = foo();
console.log(generator.next());
console.log(generator.next());
console.log(generator.next());
/*
111
222
{ value: 'bbb', done: false }
333
{ value: 'ccc', done: false }
444
555
666
777
{ value: undefined, done: true }
*/
done:true
function* foo() {
console.log(111);
console.log(222);
yield "bbb";
console.log(333);
return 0;
yield "ccc";
console.log(444);
console.log(555);
console.log(666);
console.log(777);
}
const generator = foo();
console.log(generator.next());
console.log(generator.next());
console.log(generator.next());
/*
111
222
{ value: 'bbb', done: false }
333
{ value: 0, done: true }
{ value: undefined, done: true }
*/
const 变量名 = yield 返回值
,变量是传递给下面的代码的function* foo(next1) {
console.log(111, next1);
console.log(222, next1);
const next2 = yield "bbb";
console.log(333, next2);
const next3 = yield "ccc";
console.log(444, next3);
console.log(555, next3);
console.log(666, next3);
console.log(777, next3);
}
const generator = foo("我是第一个参数");
generator.next();
generator.next("我是第二个参数");
generator.next("我是第三个参数");
/*
111 我是第一个参数
222 我是第一个参数
333 我是第二个参数
444 我是第三个参数
555 我是第三个参数
666 我是第三个参数
777 我是第三个参数
*/
迭代器中由return方法,可以是迭代器中断,因此在生成器中,我们可以直接调用return方法或者throw一个error来终止函数的执行
function* foo(next1) {
console.log(111, next1);
console.log(222, next1);
const next2 = yield "bbb";
console.log(333, next2);
const next3 = yield "ccc";
console.log(444, next3);
console.log(555, next3);
console.log(666, next3);
console.log(777, next3);
}
const generator = foo("我是第一个参数");
generator.next();
generator.return("我是第二个参数"); //后面的函数不会执行,但是next会有返回值
generator.throw(new Error("我是一个错误"))//与return执行逻辑是一样的,但是需要捕获异常
generator.next("我是第三个参数");
结合以上知识我们可以对之前写过的迭代器进行代替,用更少的代码实现相似的功能
function* createGeneratorArr(arrName) {
// index = 0;
// //返回一个对象,对象中包含next方法,所以可以链式调用
// return {
// next() {
// if (index < arrName.length) {
// return { done: false, value: arrName[index++] };
// } else {
// //遍历完成只会,done为false,value可以省略
// return { done: true };
// }
// },
// };
/*
先前的做法是通过这个函数,返回一个所谓迭代器对象,通过这个对象调用next方法
我们借助生成器函数返回生成器对象的原理
*/
for (let i = 0; i < arrName.length; i++) {
yield arrName[i];
}
}
let arr = [1, 2, 3, 4, 5, 6];
let arrGenerator = createGeneratorArr(arr);
console.log(arrGenerator.next());
console.log(arrGenerator.next());
console.log(arrGenerator.next());
/**{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false } */
在迭代器的学习中,我们在一个类中,创建了相应的方法去生成可迭代对象,同理,我们可以用生成器对代码进行优化
yield*
的机制
function* createGeneratorArr(arrName) {
// for (let i = 0; i < arrName.length; i++) {
// yield arrName[i];
// }
/*
刚刚的代码,我们使用了for循环对其进行yield控制
我们可以用yield* addName 直接替代该循环
*/
yield* arrName
}
yield*
的机制之后,我们就可以将自定义类的代码进行优化class Person {
constructor(name, age, friends) {
this.name = name;
this.age = age;
this.friends = friends;
}
//在实例对象上创建实例方法
// [Symbol.iterator]() {
// let index = 0;
// //对对象的key进行迭代
// let keys = Object.keys(this);
// const iterator = {
// next: () => {
// if (index < keys.length) {
// return { done: false, value: keys[index++] };
// } else {
// return { done: true };
// }
// },
// };
// return iterator;
// }
/*要用生成器函数*/
*[Symbol.iterator]() {
//迭代元素的属性
yield* Object.keys(this);
//迭代元素的值
yield* Object.values(this);
//迭代元素的键值对
yield* Object.entries(this);
}
}
let p = new Person("zhangcheng", 18, "jame");
for (const iterator of p) {
console.log(iterator);
}