Iterator和Genertator

一、Iterator迭代器和for of原理


* 遍历器(Iterator)是一种机制(接口):为各种不同的数据结构提供统一的访问机制,任何数据结构只要部署Iterator接口,就可以完成遍历操作「for of循环」,依次处理该数据结构的所有成员

* + 拥有next方法用于依次遍历数据结构的成员

* + 每一次遍历返回的结果是一个对象 {done:false,value:xxx}

* + done:记录是否遍历完成

* + value:当前遍历的结果

* 拥有Symbol.iterator属性的数据结构(值),被称为可被遍历的,可以基于for of循环处理

* + 数组

* + 部分类数组:arguments/NodeList/HTMLCollection...

* + String

* + Set

* + Map

* + generator object

* + ...

* 对象默认不具备Symbol.iterator,属于不可被遍历的数据结构

自己创建一个Iterator类,来实现ES6中的迭代器规范

class Iterator {
    constructor(assemble) {
        let self = this;
        self.assemble = assemble;
        self.index = 0;
    }
    next() {
        let self = this,
            assemble = self.assemble;
        if (self.index > assemble.length - 1) {
            return {
                done: true,
                value: undefined
            };
        }
        return {
            done: false,
            value: assemble[self.index++]
        };
    }
}

创建一个实例对象,其应该具备迭代器规范的要求 
  itor.next() 具备next方法,执行这个方法可以依次获取到数据结构中的每一个成员值
  每一次获取的成员值是一个对象 
    + done:是否迭代完毕
    + value:当前获取的那个值
  符合以上两个特点的对象,我们称之为符合迭代器规范的对象!!

let itor = new Iterator([10, 20, 30, 40]);
console.log(itor.next()); //->{value:10,done:false}
console.log(itor.next()); //->{value:20,done:false}
console.log(itor.next()); //->{value:30,done:false}
console.log(itor.next()); //->{value:40,done:false}
console.log(itor.next()); //->{value:undefined,done:true}

/*

 在JS中,有很多数据结构,天生具备迭代器规范,例如:

   我们主要看数据结构(对象)是否具备 Symbol.iterator 这个属性;有这个属性就具备迭代器规范,没有就不具备;具备这个规范,就可以使用 for/of 循环来迭代数据中的每一项值了!!

   + 数组  Array.prototype[Symbol(Symbol.iterator)]=function...

   + 部分类数组

     + arguments[Symbol(Symbol.iterator)]

     + NodeList.prototype[Symbol(Symbol.iterator)]

     + HTMLCollection.prototype[Symbol(Symbol.iterator)]

     + ...

   + 字符串 String.prototype[Symbol(Symbol.iterator)]

   + Set/Map

   + ...

 但是对于纯粹对象「或者自己构建的类数组对象」等来讲,默认是不具备 Symbol.iterator 这个属性的,所以他们不具备迭代器规范!「不能直接使用 for/of 循环」

 */

数组迭代的方式:for、while、forEach/map、for/in、for/of...

let arr = [10, 20, 30, 40];

arr[Symbol.iterator] = function () {
    console.log('FOR/OF START');
    let self = this, //this->arr
        index = -1;
    // 返回具备迭代器规范的对象 -> itor
    return {
        next() {
            index += 2;
            if (index >= self.length) {
                return {
                    done: true,
                    value: undefined
                };
            }
            return {
                done: false,
                value: self[index]
            };
        }
    };
};

for (let val of arr) {
    console.log(val);
} 

for of 原理

// for/of循环主要用于获取数据结构中每一项的“值”
for (let val of arr) {
    console.log(val);
} 
原理:
  1.迭代执行,先执行数组的Symbol.iterator这个方法,获取一个具备迭代器规范的对象->itor
  2.开始迭代:每一次迭代都是把 itor.next 方法执行
    + 把获取对象中的value属性值,赋值给val这个变量
    + 再看对象中done这个属性的值,如果是false,则继续迭代;如果是true,则结束迭代!!
*/

//============================
// 迭代对象的方式:for/in;获取所有的keys,然后迭代keys;也可以使用for/of(但是需要自己为其设置Symbol.iterator)
/* let obj = {
    name: 'zhufeng',
    age: 13,
    0: 100,
    [Symbol('AA')]: 200
};

Object.prototype[Symbol.iterator] = function iterator() {
    let self = this, //迭代的对象
        index = -1,
        keys = Reflect.ownKeys(self);
    return {
        next() {
            index++;
            if (index >= keys.length) {
                // 这个必须要设置,否则就是死循环了
                return {
                    done: true,
                    value: undefined
                };
            }
            let key = keys[index];
            return {
                done: false,
                value: self[key]
            };
        }
    };
};

for (let val of obj) {
    console.log(val);
} 

for (let val of obj) {
    console.log(val); //Uncaught TypeError: obj is not iterable
} 

类数组

/* // 如果是类数组,是可以直接借用数组原型上的Symbol.iterator方法的!!
let obj = {
    0: 10,
    1: 20,
    2: 30,
    length: 3
};
obj[Symbol.iterator] = Array.prototype[Symbol.iterator];
for (let val of obj) {
    console.log(val);
} */

/* 
if ( typeof Symbol === "function" ) {
    jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ];
}
*/

二、 Genertator基础和 await原理


生成器对象是由一个generator function返回的,并且它符合可迭代协议和迭代器协议
Generator - JavaScript | MDN

讲义示例:

/*
 * 普通函数 VS 生成器函数
 *    生成器函数 [[IsGenerator]]:true
 *    
 *   「把它当做一个实例 __proto__」
 *       普通函数是 Function 的实例,普通函数.__proto__===Function.prototype
 *       生成器函数是 GeneratorFunction 的实例
 *           生成器函数.__proto__===GeneratorFunction.prototype
 *           GeneratorFunction.prototype.__proto__===Function.prototype
 *      ({}).toString.call(生成器函数) => "[object GeneratorFunction]"
 *    
 *   「把它作为一个构造函数 prototype」
 *      生成器函数不能被new执行  Uncaught TypeError: func is not a constructor
 *      当做普通函数执行,返回的结果就是生成器函数的一个实例
 *      itor.__proto__ -> func.prototype「空对象,没有constructor」 -> Generator.prototype「constructor:GeneratorFunction」{next/return/throw/Symbol(Symbol.toStringTag): "Generator"} -> 一个具备迭代器规范的对象「Symbol(Symbol.iterator)」 -> Object.prototype
 */

// generator函数:function后面加一个*
function* fn() {
    console.log(this);
    return 10;
}
fn.prototype.query = function () {};
let gen = fn();
console.log(gen);
// gen.__proto__ -> fn.prototype「query」 -> GeneratorFunction.prototype「next/return/throw/Symbol.toStringTag」-> xxx.prototype「Symbol.iterator」 -> Object.prototype
console.log(typeof fn); //->"function"
console.log(fn instanceof Function); //->true 


function* generator() {
    console.log('A');
    yield 10;
    console.log('B');
    yield 20;
    console.log('C');
    yield 30;
    console.log('D');
    return 100;
}
let itor = generator();
console.log(itor.next()); //->{value:10,done:false}
console.log(itor.next()); //->{value:20,done:false}
console.log(itor.next()); //->{value:30,done:false}
console.log(itor.next()); //->{value:100,done:true}
console.log(itor.next()); //->{value:undefined,done:true} 


function* generator() {
    console.log('A');
    yield 10;
    console.log('B');
    yield 20;
    console.log('C');
    return 30;
}
let itor = generator();
console.log(itor.next()); //->{value:10,done:false}
console.log(itor.return('@return')); //->{value:"@return",done:true}
// console.log(itor.throw('@throw'));
console.log(itor.next()); //->{value:undefined,done:true}


// 每一次执行next的传递的值,是作为上一次yeild的返回值处理的
function* generator() {
    let x1 = yield 10;
    console.log(x1);
    let x2 = yield 20;
    console.log(x2);
    return 30;
}
let itor = generator();
itor.next('@1');
itor.next('@2');
itor.next('@3');


// yeild* 后面跟着一个新的itor,后期执行到这的时候,会进入到新的generator中执行
function* generator1() {
    yield 10;
    yield 20;
}

function* generator2() {
    yield 10;
    yield* generator1();
    yield 20;
}
let itor = generator2();
console.log(itor.next()); //value:10  done:false
console.log(itor.next()); //value:10 done:false
console.log(itor.next()); //value:20  done:false
console.log(itor.next()); //value:20 done:false
console.log(itor.next()); //value:undefined done:true 

如何创建一个Generator生成器函数?

   + 把创建函数的“function”后面加一个 “*” 即可

   + 箭头函数是无法变为生成器函数的

 每一个生成器函数,都是GeneratorFunction这个类的实例

   fn.__proto__ -> GeneratorFunction.prototype -> Function.prototype

   多了这样的一个私有属性 [[IsGenerator]]:true

 普通函数的原型链

   fn.__proto__ -> Function.prototype

 

 当生成器函数执行:

   + 首先并不会立即把函数体中的代码执行

   + 而是返回一个具备迭代器规范的对象「itor」

     itor.__proto__

     + next

     + throw

     + return

     + Symbol(Symbol.iterator) : function...

     + ...

   + 当进行itor.next()执行的时候

     + 把函数体中的代码开始执行

     + 返回一个对象

       + done:记录代码是否执行完毕

       + value:记录本次处理的结果

const fn = function* fn() {
    console.log("代码运行中:", 10);
    return 100;
};
let itor = fn();
console.log(itor.next()); //-> {done:true,value:100}
console.log(itor.next()); //-> {done:true,value:undefined}

对于ES6快捷赋值的语法,我们在方法名前面设置*,就可以创建生成器函数了

let obj = {

    // sum:function(){}

    *sum() {

    }

};

console.log(obj.sum());

 Generator生成器函数的作用:

   可以基于返回的itor(迭代器对象),基于其next方法,控制函数体中的代码,一步步的去执行!!

   + 每一次执行next,控制函数体中的代码开始执行「或者从上一次暂停的位置继续执行」,遇到yield则暂停!!

     done:false

     value:yield后面的值

   + 当遇到函数体中的return,或者已经执行到函数最末尾的位置了

     done:true

     value:函数的返回值或者undefined

const fn = function* fn() {
    console.log('A');
    yield 100;
    console.log('B');
    yield 200;
    console.log('C');
    yield 300;
    console.log('D');
    return 400;
};
let itor = fn();
console.log(itor.next()); //->{done:false,value:100}
console.log(itor.next()); //->{done:false,value:200}
console.log(itor.next()); //->{done:false,value:300}
console.log(itor.next()); //->{done:true,value:400}

--------------------------------------------------------

const fn = function* fn() {
    console.log('A');
    yield 100;
    console.log('B');
    yield 200;
    console.log('C');
    yield 300;
    console.log('D');
    return 400;
};
let itor = fn();
console.log(itor.next());  // -> {done:false, value:100}
console.log(itor.return('哈哈哈')); // -> {value:'哈哈哈', done:true} 相当于在函数体中执行遇到了return,结束整个函数的继续执行「done:true」,传递的值相当于return的值!!
console.log(itor.next());  // -> {value:undefined, done:true}


console.log(itor.throw('哈哈哈')); // 手动抛出异常「控制台报红」;生成器函数中的代码,都不会再执行了!!
console.log(itor.next()); // 抛出异常后,它下面的代码也不会执行了
console.log('我是的外边的');

 params:生成器函数接收的实参值,它是生成器函数执行时传递的值

   fn(10,20,30)

   params:[10,20,30]

 itor.next(N):每一次执行next方法,传递的值会作为上一个yield的返回值「所以第一次执行next方法,传递的值是没有用的,因为在它之前没有yield」

const fn = function* fn(...params) {
    let x = yield 100;
    console.log(x); //'second:222'
    let y = yield 200;
    console.log(y); //'three:333'
};
let itor = fn(10, 20, 30);
console.log(itor.next('first:111')); //-> {value: 100, done: false}
console.log(itor.next('second:222')); //-> {value: 200, done: false}
console.log(itor.next('three:333')); //-> {value: undefined, done: true} 

 生成器嵌套问题  yield*

const sum = function* sum() {
    yield 300;
    yield 400;
};
const fn = function* fn() {
    yield 100;
    yield* sum(); // yield*:支持让我们进入另外一个生成器函数中去一步步的执行 
    yield 200;
};
let itor = fn();
console.log(itor.next()); //-> {value: 100, done: false}
console.log(itor.next()); //-> {value: 300, done: false}
console.log(itor.next()); //-> {value: 400, done: false}
console.log(itor.next()); //-> {value: 200, done: false}
console.log(itor.next()); //-> {value: undefined, done: true}

// -------------------------

异步

const delay = (interval = 1000) => {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(`@@${interval}`);
        }, interval);
    });
};

需求:串行请求,有三个请求「请求需要的时间分别是 1000/2000/3000」?

delay(1000)
    .then(value => {
        console.log('第一个请求成功:', value);
        return delay(2000);
    })
    .then(value => {
        console.log('第二个请求成功:', value);
        return delay(3000);
    })
    .then(value => {
        console.log('第三个请求成功:', value);
    })
    .catch(reason => {
        console.log('任何请求失败,都执行这里:', reason);
    });
(async () => {
    try {
        let value = await delay(1000);
        console.log('第一个请求成功:', value);

        value = await delay(2000);
        console.log('第二个请求成功:', value);

        value = await delay(3000);
        console.log('第三个请求成功:', value);
    } catch (reason) {
        console.log('任何请求失败,都执行这里:', reason);
    }
})();

基于Generator函数,模拟Await的语法,实现请求的串行

const handle = function* handle() {
    let value = yield delay(1000);
    console.log('第一个请求成功:', value);

    value = yield delay(2000);
    console.log('第二个请求成功:', value);

    value = yield delay(3000);
    console.log('第三个请求成功:', value);
};

编写通知Generator中代码逐一执行的方法

ES8(ECMAScript2017)中,提供了async/await语法:用来简化promise的操作

   它是 Promise + Generator 的语法糖

   我们自己上边实现的AsyncFunction和Generator函数就是async/await的原理!!

const AsyncFunction = function AsyncFunction(generator, ...params) {
    let itor = generator(...params);
    // 基于递归的方法,通知Generator函数中的代码逐一执行
    const next = x => {
        let { done, value } = itor.next(x);
        if (done) return;
        if (!(value instanceof Promise)) value = Promise.resolve(value);
        value.then(next);
    };
    next();
};

// AsyncFunction(handle);
AsyncFunction(function* (x, y) {
    let total = x + y;
    let value = yield total;
    console.log('@1-->', value);

    yield delay(2000);
    console.log('@2-->', '哈哈哈');
}, 10, 20);

/* (async (x, y) => {
    let total = x + y;
    let value = await total;
    console.log('@1-->', value);

    await delay(2000);
    console.log('@2-->', '哈哈哈');
})(10, 20); */

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值