文章目录
引言
我们常常需要遍历一个对象,例如访问数组里的值,字符串中的值,arguments里的值等。我么常用let...of...
来对对象进行遍历,而这个方法的底层实现就是迭代器(Iterator
)
迭代器
ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator
属性,就可以认为是“可遍历的”(iterable
)。而迭代器的本质,就是内部实现了一个next()方法,返回不同的数据。
生具备 Iterator 接口的数据结构如下:Array, String, map, set, string (对象不可以哦!!!)
let arr = [1, 2 ,3]
for(let item of arr){
console.log(arr);
} // 可以正常输出,1, 2, 3
let obj = {name: 'huang'}
for(let item of obj){
console.log(item);
} // 不可以正常输出,报错:obj is not iterable
// 可以在控制台打印一下,查看二者是否具备[Symbol.iterator]属性
迭代器的使用
获取具体的迭代器对象,然后调用迭代器身上的next()方法,就可以逐步拿到数据
let arr = [1, 2, 3]
console.log(arr);
let Iter = arr[Symbol.iterator]()
console.log(Iter.next()); // {value: 1, done: false}
console.log(Iter.next()); // {value: 2, done: false}
console.log(Iter.next()); // {value: 3, done: false}
console.log(Iter.next()); // {value: undefined, done: true}
自定义迭代器
总结:只要给一个对象实现了Symbol.iterator方法,let...of...
就可以迭代。这个迭代的本质,就是借用了里面的next()方法来实现。
class MyArray {
constructor() {
for (let i = 0; i < arguments.length; i++) {
this[i] = arguments[i];
}
this.length = arguments.length;
}
[Symbol.iterator]() {
let index = 0;
let that = this;
return {
next: function () {
return index < that.length ?
{value: that[index++], done: false}:
{value: undefined, done: true}
}
}
}
}
let arr = new MyArray(1, 2, 3);
let Iter = arr[Symbol.iterator]()
console.log(Iter.next());
console.log(Iter.next());
console.log(Iter.next());
console.log(Iter.next());
for(let item of arr){
console.log(item); // 1, 2, 3
}
// 如果知识让你实现next方法,就不用自己定义数组类了,直接定义一个构造函数即可。
function MyIterator(array){
var idx = 0;
return {
next(){
return idx < array.length ?
{value: array[idx++], done: false} :
{value: undefined, done: true}
}
}
}
let Iter = MyIterator([1, 2, 3, 4, 5]);
把普通对象转化为可迭代对象
普通对象默认没有配置Iterator,所以不可以迭代
let obj = {
name: "lnj",
age: 34,
gender: "man",
[Symbol.iterator]() {
let keys = Object.keys(this);
let index = 0;
let that = this;
return {
next() {
return index < keys.length ?
{ value: that[keys[index++]], done: false } :
{ value: undefined, done: true }
}
}
}
}
let it = obj[Symbol.iterator]();
console.log(it.next());
console.log(it.next());
console.log(it.next());
console.log(it.next());
for (let value of obj) {
console.log(value); // 'lnj', 34, 'man' 当然返回什么可以自己定义
}
把类数组对象转化为可迭代对象
注意:普通对象不能直接利用Symbol.iterator,因为普通对象没有索引值,所以Keys需要自己去获取
let obj = {
0: 1,
1: 2,
2: 3,
length: 3,
[Symbol.iterator]: Array.prototype[Symbol.iterator]
}
for (let item of obj) {
console.log(item); // 1 2 3
}
注意:let...of..., 解构赋值, 拓展运算符都是Iterator的应用场景,因为他们都需要迭代对象的内容
生成器
Generator 函数是 ES6 提供的一种异步编程解决方案,Generator 函数内部可以封装多个状态, 因此又可以理解为是一个状态机。只需要在普通函数的function后面加上*,该函数就会变为Generator 函数,通过yield定义状态,并且yield关键字可以让 Generator内部的逻辑能够切割成多个部分。yield可以通过调用迭代器的next函数,不断执行定义的状态
生成器可以定义多个状态,在不同的时机调用
生成器可以返回迭代器对象,可以用来简写迭代器的写法
基本用法
每一次调用迭代器的next方法,都会调用相应的yield声明的状态前的代码片段。
如果写了return,也是如此,只不过,后面的yield从此都不会被使用到了
function* test(){
const res1 = yield 1;
console.log(res1); // two
const res2 = yield 2;
console.log(res2); // three
const res3 =yield 3;
console.log(res3); // four
}
let iter = test();
console.log(iter.next('one'));
console.log(iter.next('two'));
console.log(iter.next('three'));
console.log(iter.next('four'));
function *test(){
yield 1;
yield 2;
yield 3;
yield 4;
yield 5;
}
let iter = test();
// 这里也可以直接用Let...of...进行遍历
// for(let key of iter){
// console.log(key); // 1 2 3 4 5
// }
console.log(iter.next()); // { value: 1, done: false }
console.log(iter.next()); // { value: 2, done: false }
console.log(iter.next()); // { value: 3, done: false }
console.log(iter.next()); // { value: 4, done: false }
console.log(iter.next()); // { value: 5, done: false }
console.log(iter.next()); // { value: undefined, done: true }
返回多个结果
function* test(a, b) {
yield a + b
yield a - b
}
let Iter = test(1, 2)
console.log(Iter.next()); // {value: 3, done: false}
console.log(Iter.next()); // {value: -1, done: false}
用生成器使对象可遍历
上面把对象转化为可迭代对象
是利用自己定义的next方法。但是,生成器本来就是用来生成迭代器的,所以可以使上面的写法更加简单
let obj = {
name: "lnj",
age: 34,
gender: "man",
[Symbol.iterator]: function* () {
let index = 0;
let keys = Object.keys(this)
while(index < keys.length) {
yield this[keys[index++]]
}
}
}
let it = obj[Symbol.iterator]();
console.log(it.next()); // {value: 'lnj', done: false}
console.log(it.next()); // {value: 34, done: false}
console.log(it.next()); // {value: 'man', done: false}
console.log(it.next()); // {value: undefined, done: true}
for (let value of obj) {
console.log(value); // 'lnj', 34, 'man' 当然返回什么可以自己定义
}
Promise Generator async/await解决异步问题简要记录
问题的出现来自于回调地狱
function request(fn) {
setTimeout(() => {
fn('拿到的数据') // 发起了异步请求,拿到了数据,作为了fn的回调参数
}, 3000);
}
request(function (data) {
console.log('我是第一层', data);
request(function (data) {
console.log('我是第二层', data);
request(function (data) {
console.log('我是第三层', data);
})
})
})
Promise改写
看起来规整了一点
function request(){
return new Promise((resolve, reject) => {
setTimeout(() => resolve('拿到的数据'), 1000)
})
}
request().then((data)=>{
console.log('我是第一层', data);
return request()
}).then((data)=>{
console.log('我是第二层', data);
return request()
}).then((data)=>{
console.log('我是第三层', data);
return request()
})
Generator改写
Generator也是ES6提出的解决异步编程的方案,和Promise不属于同一技术(我目前认为)
function request(fn) {
setTimeout(() => {
fn('拿到的数据') // 发起了异步请求,拿到了数据,作为了fn的回调参数
}, 3000);
}
function* test(){
yield request(function (data) {console.log('我是第一层', data);})
yield request(function (data) {console.log('我是第二层', data);})
yield request(function (data) {console.log('我是第三层', data);})
}
let Iter = test();
// 三秒后这三个会同时出现, 但是我就是可以控制它什么时候有,什么时候没有
Iter.next()
Iter.next()
Iter.next()
Async/Await
简单理解:是Generator的语法糖, async替代*, await 替代yield, 并对其他的一些细节做了优化,例如Async函数的返回值是Promise包装过的,而Generator返回对象是Iterator。注意:await后如果异常了,需要自己去try...catch...
,而Promise提供了catch方法,且Promise提供了更多的方法,例如Promise.all等
这种写法让人看起来最最舒服了!!!!
function request() {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(2000);
},1000)
})
// 当然也是可以不用return Promise对象的。。。会帮你转化为Promise
// return 1
}
async function test() {
let res1 = await request();
console.log('第一层数据', res1);
let res2 = await request();
console.log('第二层数据', res2);
let res3 = await request();
console.log('第三层数据', res3);
}
let res = test()
console.log(res) // 返回的是Promise;