ES6遍历器

遍历

  • Object.keys():遍历出自身的可枚举的键名(不含继承属性)
  • Object.values():遍历出自身的可枚举的键值(不含继承属性)
  • Object.entries():遍历出自身的可枚举的键值对(不含继承属性)
  1. const foo = {
  2. a: 1,
  3. b: 2,
  4. c: 3
  5. }
  6. Object.defineProperties(foo, {
  7. d: {
  8. value: 4,
  9. enumerable: true
  10. },
  11. f: {
  12. value: 5,
  13. enumerable: false
  14. }
  15. })
  16. //获取当前对象的键名,不能遍历未被定义枚举属性的键名
  17. // console.log(Object.keys(foo));
  18. //["a", "b", "c", "d"]
  19. //获取当前对象的键值,不能遍历未被定义枚举属性的键值
  20. // console.log(Object.values(foo));
  21. //[1, 2, 3, 4]
  22. //获取当前对象的键值对,不能遍历未被定义枚举属性的键值
  23. // console.log(Object.entries(foo));
  24. /**
  25. * [Array(2), Array(2), Array(2), Array(2)]
  26. * 0: (2) ["a", 1]
  27. * 1: (2) ["b", 2]
  28. * 2: (2) ["c", 3]
  29. * 3: (2) ["d", 4]
  30. * __proto__: Array(0)
  31. */

for of

ES6为了解决迭代器iterator().next()调用繁琐而新增的API

可以遍历对应的数据结构,通过该API可以迭代

  1. let arr = [1, 2, 3];
  2. for (let i of arr) {
  3. console.log(i); //1 2 3
  4. }

for in区别:

  • for in:拿到的是数据结构的下标(主要遍历对象)
  • for of:拿到的是数据结构的值(部署过迭代器的数据接口的数据类型的)

生成器

generator函数语法

作用是访问迭代器对象,返回值是迭代器对象

  1. function* test(){}

只要执行生成器函数 生成一个迭代器对象

  1. function* test() {}
  2. console.log(test());
  3. /**
  4. * 生成跟迭代器对象里具有next()方法的对象
  5. * test {<suspended>}
  6. * __proto__: Generator
  7. * __proto__: Generator
  8. * constructor: GeneratorFunction {...}
  9. * next: ƒ next()
  10. */

生成器函数一定要和yeild结合使用,可以自定义产出内容和返回值,用法和迭代器有点像

yeild:产出的意思,产出的同时暂停程序向下执行

  1. function* test() {
  2. yield 'a';
  3. yield 'b';
  4. yield 'c';
  5. return 'd';
  6. }
  7. let iter = test();
  8. console.log(iter.next()); //{value: "a", done: false}
  9. console.log(iter.next()); //{value: "b", done: false}
  10. console.log(iter.next()); //{value: "c", done: false}
  11. console.log(iter.next()); //{value: "d", done: true}

迭代器接口迭代yeild所产出的值

  1. function* test() {
  2. console.log(0); //0
  3. yield 'a'; //{value: "a", done: false}
  4. console.log(1); //1
  5. yield 'b'; //{value: "b", done: false}
  6. console.log(2); //2
  7. yield 'c'; //{value: "c", done: false}
  8. console.log(3); //3
  9. return 'd'; //{value: "d", done: true}
  10. }
  11. let iter = test();
  12. console.log(iter.next());
  13. console.log(iter.next());
  14. console.log(iter.next());
  15. console.log(iter.next());

yeildreturn本质的区别:

  • yeild暂停,找上一次暂停的位置,有记忆功能
  • return结束程序执行

返回值问题

  1. //说明yeild并不产出值
  2. function* test() {
  3. let a = yield 'a';
  4. console.log(a);
  5. yield 'b';
  6. yield 'c';
  7. return 'd';
  8. }
  9. let iter = test();
  10. //第一次执行,不产出yeild的返回值
  11. console.log(iter.next()); //{value: "a", done: false}
  12. //第二次执行,产出yeild的返回值为undefined
  13. console.log(iter.next()); //undefined {value: "b", done: false}
  1. //如果想要yeild产出值,可以在next()执行时传参
  2. console.log(iter.next(10)); //10 {value: "b", done: false}

next()传值问题 蛇形传值方式

  1. function* foo() {
  2. let value1 = yield 1;
  3. console.log(value1);
  4. let value2 = yield 2;
  5. console.log(value2);
  6. let value3 = yield 3;
  7. console.log(value3);
  8. let value4 = yield 4;
  9. console.log(value4);
  10. }
  11. let iter = foo();
  12. //第一次执行,不产出yeild的返回值
  13. console.log(iter.next('one')); //{value: 1, done: false}
  14. //第二次执行,产出yeild的返回值为第一次执行next()传入的值
  15. console.log(iter.next('two')); //two {value: 1, done: false}

优化对象迭代器的函数

  1. let obj = {
  2. start: [1, 2, 3],
  3. end: [7, 8, 9],
  4. [Symbol.iterator]: function* () {
  5. var nextIndex = 0,
  6. arr = [...this.start, ...this.end],
  7. len = arr.length;
  8. while (nextIndex < len) {
  9. //产出值
  10. yield arr[nextIndex++];
  11. }
  12. }
  13. }
  14. for (let i of obj) {
  15. console.log(i); //1 2 3 4 5 6 7 8 9
  16. }

利用生成器函数优化代码

  1. function promisify(fn) {
  2. return function (...args) {
  3. return new Promise((resolve, reject) => {
  4. fn(...args, (err, data) => {
  5. if (err) {
  6. reject(err);
  7. } else {
  8. resolve(data);
  9. }
  10. });
  11. });
  12. };
  13. }
  14. let readFile = promisify(fs.readFile);
  15. function* read() {
  16. let value1 = yield readFile("./name.txt", "utf-8");
  17. let value2 = yield readFile(value1, "utf-8");
  18. let value3 = yield readFile(value2, "utf-8");
  19. }
  20. let iter = read();
  21. //格式:let {value: xx, done: false} = iter.next();
  22. // let { value: value, done: done } = iter.next();
  23. let { value, done } = iter.next();
  24. //进一步调用next(val)拿到第一次的值
  25. value.then((val1) => {
  26. let { value: done } = iter.next(val1);
  27. value.then((val2) => {
  28. let { value, done } = iter.next(val2);
  29. value.then((val3) => {
  30. console.log(val3);
  31. });
  32. });
  33. });
  1. //以上写法过于繁琐,优化提纯一个新的函数
  2. //解决链式调用的问题
  3. function Co(iter) {
  4. return new Promise((resolve, reject) => {
  5. let next = (data) => {
  6. let { value, done } = iter.next(data);
  7. if (done) {
  8. resolve(value);
  9. } else {
  10. value.then((val) => {
  11. next(val);
  12. });
  13. }
  14. };
  15. next();
  16. });
  17. }
  18. let promise = Co(read());
  19. promise.then((val) => console.log(val));

生成器对象上的return()/throw()方法

  1. //return()
  2. //终结迭代的方式,终结遍历这个函数
  3. //调用之后返回true
  4. function* get() {
  5. yield 1;
  6. yield 2;
  7. yield 3;
  8. }
  9. let g = get();
  10. console.log(g.next());
  11. //{value: 1, done: false}
  12. console.log(g.next());
  13. //{value: 2, done: false}
  14. console.log(g.next());
  15. //{value: 3, done: false}
  16. console.log(g.next());
  17. //{value: undefined, done: true}
  18. let h = get();
  19. console.log(h.next());
  20. //{value: 1, done: false}
  21. console.log(h.return());
  22. //{value: undefined, done: true}
  23. console.log(h.next());
  24. //{value: undefined, done: true}
  25. console.log(h.next());
  26. //{value: undefined, done: true}
  1. //throw()必须在next()之后执行才能捕获异常
  2. //throw()也相当于next()继续执行迭代
  3. //try...catch对异步代码不管用
  4. var g = function* () {
  5. try {
  6. yield;
  7. } catch (e) {
  8. console.log("生成器内部异常:" + e);
  9. }
  10. };
  11. var i = g();
  12. console.log(i.throw("a")); //无法捕获错误
  13. console.log(i.next());
  14. console.log(i.throw("a")); //捕获成功

迭代器

Symbol底下有一个iterator()方法是迭代器,且迭代器里有个next()方法

  1. let arr = [1, 2, 3];
  2. console.log(arr);
  3. //[1, 2, 3]
  4. //数组的原型上找到Symbol构造函数里的iterator方法并执行
  5. let iter = arr[Symbol.iterator]();
  6. //Symbol.iterator方法的原型上有一个next()方法
  7. console.log(iter.next()); //{value: 1, done: false}
  8. console.log(iter.next()); //{value: 2, done: false}
  9. console.log(iter.next()); //{value: 3, done: false}
  10. console.log(iter.next()); //{value: undefined, done: true}

以上说明迭代的本质也在读取数据结构的值,抽取当中结构数据的第一个值,有序的,连续的

迭代器是一种有序的,连续的,基于拉取的一种消耗数据的组织方式

具有部署迭代器接口的数据结构有:

Array/Map/Set/weekMap/weekSet/arguments/nodeList

  1. /**
  2. * 模拟一个迭代器函数
  3. * @param {*} array 数组
  4. * @returns 返回一个next()方法
  5. */
  6. function makeIterator(array) {
  7. //指针
  8. var nextIndex = 0;
  9. return {
  10. next: function () {
  11. return nextIndex < array.length ? {
  12. value: array[nextIndex++],
  13. done: false
  14. } : {
  15. value: undefined,
  16. done: true
  17. }
  18. }
  19. }
  20. }
  21. //调用
  22. var iter = makeIterator([1, 2, 3]);
  23. console.log(iter.next()); //{value: 1, done: false}
  24. console.log(iter.next()); //{value: 2, done: false}
  25. console.log(iter.next()); //{value: 3, done: false}
  26. console.log(iter.next()); //{value: undefined, done: true}

迭代器进阶

迭代器实现方式

  1. function makeIterator(arr) {
  2. //如果没有迭代完成,生成一个索引
  3. var nextIndex = 0;
  4. //返回一个迭代器对象
  5. return {
  6. //迭代器对象里有个next()方法
  7. next() {
  8. if (nextIndex < arr.length) {
  9. return {
  10. value: arr[nextIndex++],
  11. done: false
  12. }
  13. }
  14. return {
  15. value: undefined,
  16. done: true
  17. }
  18. }
  19. }
  20. }
  21. var it = makeIterator(['a', 'b']);
  22. console.log(it.next()); //{value: "a", done: false}
  23. console.log(it.next()); //{value: "b", done: false}
  24. console.log(it.next()); //{value: undefined, done: true}
  1. //因为对象不具备迭代器接口
  2. //部署迭代器方式生成一个外部迭代器便于for of遍历
  3. let obj = {
  4. start: [1, 2, 3],
  5. end: [7, 8, 9],
  6. [Symbol.iterator]() {
  7. var nextIndex = 0,
  8. arr = [...this.start, ...this.end],
  9. len = arr.length;
  10. return {
  11. next() {
  12. if (nextIndex < len) {
  13. return {
  14. value: arr[nextIndex++],
  15. done: false
  16. }
  17. } else {
  18. return {
  19. value: undefined,
  20. done: true
  21. }
  22. }
  23. }
  24. }
  25. }
  26. }
  27. for (let i of obj) {
  28. console.log(i);
  29. }

为什么对象身上不具备迭代器接口?

因为对象上键值对成员是无序的

  1. let map = new Map([
  2. ['a', 1],
  3. ['b', 2]
  4. ]);
  5. for (let i of map) {
  6. console.log(i);
  7. }
  8. //["a", 1]
  9. //["b", 2]

如何让对象也像Map数据结构一样具有迭代器接口?

  1. //利用map特性部署迭代器接口
  2. let obj = {
  3. a: 1,
  4. b: 2,
  5. c: 3,
  6. [Symbol.iterator]() {
  7. let nextIndex = 0;
  8. let map = new Map();
  9. for (let [key, value] of Object.entries(this)) {
  10. // console.log(key, value); //a 1 b 2 c 3
  11. //重组map
  12. map.set(key, value);
  13. }
  14. // console.log(map);
  15. //{"a" => 1, "b" => 2, "c" => 3}
  16. //将map转换为数组以便于拿到length属性
  17. //把具有迭代器对象的map展开并存入数组
  18. let mapEntries = [...map.entries()];
  19. // console.log(mapEntries);
  20. //[['a', 1], ['b', 2], ['c', 3]]
  21. //部署iterator接口
  22. return {
  23. next() {
  24. if (nextIndex < mapEntries.length) {
  25. return {
  26. value: mapEntries[nextIndex++],
  27. done: false
  28. };
  29. } else {
  30. return {
  31. value: undefined,
  32. done: true
  33. };
  34. }
  35. }
  36. }
  37. }
  38. }
  39. for (let i of obj) {
  40. console.log(i);
  41. }
  42. //["a", 1]
  43. //["b", 2]
  44. //["c", 3]

默认调用iterator接口的场合:

  • ...拓展运算符
  • for of
  • Array.from()
  • map
  • set
  • Promiss.all()
  • yeild