ES6遍历器
遍历
Object.keys():遍历出自身的可枚举的键名(不含继承属性)Object.values():遍历出自身的可枚举的键值(不含继承属性)Object.entries():遍历出自身的可枚举的键值对(不含继承属性)
const foo = {a: 1,b: 2,c: 3}Object.defineProperties(foo, {d: {value: 4,enumerable: true},f: {value: 5,enumerable: false}})//获取当前对象的键名,不能遍历未被定义枚举属性的键名// console.log(Object.keys(foo));//["a", "b", "c", "d"]//获取当前对象的键值,不能遍历未被定义枚举属性的键值// console.log(Object.values(foo));//[1, 2, 3, 4]//获取当前对象的键值对,不能遍历未被定义枚举属性的键值// console.log(Object.entries(foo));/*** [Array(2), Array(2), Array(2), Array(2)]* 0: (2) ["a", 1]* 1: (2) ["b", 2]* 2: (2) ["c", 3]* 3: (2) ["d", 4]* __proto__: Array(0)*/
for of
ES6为了解决迭代器iterator().next()调用繁琐而新增的API
可以遍历对应的数据结构,通过该API可以迭代
let arr = [1, 2, 3];for (let i of arr) {console.log(i); //1 2 3}
与for in区别:
for in:拿到的是数据结构的下标(主要遍历对象)for of:拿到的是数据结构的值(部署过迭代器的数据接口的数据类型的)
生成器
generator函数语法
作用是访问迭代器对象,返回值是迭代器对象
function* test(){}
只要执行生成器函数 生成一个迭代器对象
function* test() {}console.log(test());/*** 生成跟迭代器对象里具有next()方法的对象* test {<suspended>}* __proto__: Generator* __proto__: Generator* constructor: GeneratorFunction {...}* next: ƒ next()*/
生成器函数一定要和yeild结合使用,可以自定义产出内容和返回值,用法和迭代器有点像
yeild:产出的意思,产出的同时暂停程序向下执行
function* test() {yield 'a';yield 'b';yield 'c';return 'd';}let iter = test();console.log(iter.next()); //{value: "a", done: false}console.log(iter.next()); //{value: "b", done: false}console.log(iter.next()); //{value: "c", done: false}console.log(iter.next()); //{value: "d", done: true}
迭代器接口迭代yeild所产出的值
function* test() {console.log(0); //0yield 'a'; //{value: "a", done: false}console.log(1); //1yield 'b'; //{value: "b", done: false}console.log(2); //2yield 'c'; //{value: "c", done: false}console.log(3); //3return 'd'; //{value: "d", done: true}}let iter = test();console.log(iter.next());console.log(iter.next());console.log(iter.next());console.log(iter.next());
yeild和return本质的区别:
yeild暂停,找上一次暂停的位置,有记忆功能return结束程序执行
返回值问题
//说明yeild并不产出值function* test() {let a = yield 'a';console.log(a);yield 'b';yield 'c';return 'd';}let iter = test();//第一次执行,不产出yeild的返回值console.log(iter.next()); //{value: "a", done: false}//第二次执行,产出yeild的返回值为undefinedconsole.log(iter.next()); //undefined {value: "b", done: false}
//如果想要yeild产出值,可以在next()执行时传参console.log(iter.next(10)); //10 {value: "b", done: false}
next()传值问题 蛇形传值方式
function* foo() {let value1 = yield 1;console.log(value1);let value2 = yield 2;console.log(value2);let value3 = yield 3;console.log(value3);let value4 = yield 4;console.log(value4);}let iter = foo();//第一次执行,不产出yeild的返回值console.log(iter.next('one')); //{value: 1, done: false}//第二次执行,产出yeild的返回值为第一次执行next()传入的值console.log(iter.next('two')); //two {value: 1, done: false}
优化对象迭代器的函数
let obj = {start: [1, 2, 3],end: [7, 8, 9],[Symbol.iterator]: function* () {var nextIndex = 0,arr = [...this.start, ...this.end],len = arr.length;while (nextIndex < len) {//产出值yield arr[nextIndex++];}}}for (let i of obj) {console.log(i); //1 2 3 4 5 6 7 8 9}
利用生成器函数优化代码
function promisify(fn) {return function (...args) {return new Promise((resolve, reject) => {fn(...args, (err, data) => {if (err) {reject(err);} else {resolve(data);}});});};}let readFile = promisify(fs.readFile);function* read() {let value1 = yield readFile("./name.txt", "utf-8");let value2 = yield readFile(value1, "utf-8");let value3 = yield readFile(value2, "utf-8");}let iter = read();//格式:let {value: xx, done: false} = iter.next();// let { value: value, done: done } = iter.next();let { value, done } = iter.next();//进一步调用next(val)拿到第一次的值value.then((val1) => {let { value: done } = iter.next(val1);value.then((val2) => {let { value, done } = iter.next(val2);value.then((val3) => {console.log(val3);});});});
//以上写法过于繁琐,优化提纯一个新的函数//解决链式调用的问题function Co(iter) {return new Promise((resolve, reject) => {let next = (data) => {let { value, done } = iter.next(data);if (done) {resolve(value);} else {value.then((val) => {next(val);});}};next();});}let promise = Co(read());promise.then((val) => console.log(val));
生成器对象上的return()/throw()方法
//return()//终结迭代的方式,终结遍历这个函数//调用之后返回truefunction* get() {yield 1;yield 2;yield 3;}let g = get();console.log(g.next());//{value: 1, done: false}console.log(g.next());//{value: 2, done: false}console.log(g.next());//{value: 3, done: false}console.log(g.next());//{value: undefined, done: true}let h = get();console.log(h.next());//{value: 1, done: false}console.log(h.return());//{value: undefined, done: true}console.log(h.next());//{value: undefined, done: true}console.log(h.next());//{value: undefined, done: true}
//throw()必须在next()之后执行才能捕获异常//throw()也相当于next()继续执行迭代//try...catch对异步代码不管用var g = function* () {try {yield;} catch (e) {console.log("生成器内部异常:" + e);}};var i = g();console.log(i.throw("a")); //无法捕获错误console.log(i.next());console.log(i.throw("a")); //捕获成功
迭代器
Symbol底下有一个iterator()方法是迭代器,且迭代器里有个next()方法
let arr = [1, 2, 3];console.log(arr);//[1, 2, 3]//数组的原型上找到Symbol构造函数里的iterator方法并执行let iter = arr[Symbol.iterator]();//Symbol.iterator方法的原型上有一个next()方法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}
以上说明迭代的本质也在读取数据结构的值,抽取当中结构数据的第一个值,有序的,连续的
迭代器是一种有序的,连续的,基于拉取的一种消耗数据的组织方式
具有部署迭代器接口的数据结构有:
Array/Map/Set/weekMap/weekSet/arguments/nodeList
/*** 模拟一个迭代器函数* @param {*} array 数组* @returns 返回一个next()方法*/function makeIterator(array) {//指针var nextIndex = 0;return {next: function () {return nextIndex < array.length ? {value: array[nextIndex++],done: false} : {value: undefined,done: true}}}}//调用var iter = makeIterator([1, 2, 3]);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}
迭代器进阶
迭代器实现方式
function makeIterator(arr) {//如果没有迭代完成,生成一个索引var nextIndex = 0;//返回一个迭代器对象return {//迭代器对象里有个next()方法next() {if (nextIndex < arr.length) {return {value: arr[nextIndex++],done: false}}return {value: undefined,done: true}}}}var it = makeIterator(['a', 'b']);console.log(it.next()); //{value: "a", done: false}console.log(it.next()); //{value: "b", done: false}console.log(it.next()); //{value: undefined, done: true}
//因为对象不具备迭代器接口//部署迭代器方式生成一个外部迭代器便于for of遍历let obj = {start: [1, 2, 3],end: [7, 8, 9],[Symbol.iterator]() {var nextIndex = 0,arr = [...this.start, ...this.end],len = arr.length;return {next() {if (nextIndex < len) {return {value: arr[nextIndex++],done: false}} else {return {value: undefined,done: true}}}}}}for (let i of obj) {console.log(i);}
为什么对象身上不具备迭代器接口?
因为对象上键值对成员是无序的
let map = new Map([['a', 1],['b', 2]]);for (let i of map) {console.log(i);}//["a", 1]//["b", 2]
如何让对象也像Map数据结构一样具有迭代器接口?
//利用map特性部署迭代器接口let obj = {a: 1,b: 2,c: 3,[Symbol.iterator]() {let nextIndex = 0;let map = new Map();for (let [key, value] of Object.entries(this)) {// console.log(key, value); //a 1 b 2 c 3//重组mapmap.set(key, value);}// console.log(map);//{"a" => 1, "b" => 2, "c" => 3}//将map转换为数组以便于拿到length属性//把具有迭代器对象的map展开并存入数组let mapEntries = [...map.entries()];// console.log(mapEntries);//[['a', 1], ['b', 2], ['c', 3]]//部署iterator接口return {next() {if (nextIndex < mapEntries.length) {return {value: mapEntries[nextIndex++],done: false};} else {return {value: undefined,done: true};}}}}}for (let i of obj) {console.log(i);}//["a", 1]//["b", 2]//["c", 3]
默认调用iterator接口的场合:
...拓展运算符for ofArray.from()mapsetPromiss.all()yeild
