for...of, Iterator, Generator

for…of

ES6新增的 for…of 循环可以很方便对Array、Set、Map、类似数组的对象进行遍历,相较于数组的 forEach函数,它可以随时中断循环的进行,在数据量大的时候可以很好的提高性能

const arr = [1, 2, 3, 4, 5]; // 1, 2, 3, 4, 5
const set = new Set(arr); // 1, 2, 3, 4, 5
const map = new Map([['a', 1], ['b', 2]]); // [ 'a', 1 ], [ 'b', 2 ]

但这么好用的循环在遍历 Object的时候竟然会报错!

const obj = {
  a: 1,
  b: 2
}
for (const i of obj) { // Uncaught TypeError: obj is not iterable
  console.log(i);
}

obj变量是不可迭代的???

Iterator

精通js的都知道,for…of 判断一个变量能否遍历是看这个遍历有没有部署 iterator接口,for…of内部会调用变量的 Symbol.iterator属性(方法),该方法会返回一个指针对象,这个指针对象具有一个 next方法,调用 next方法会返回一个包含 value和 done两个属性的对象,value为当前循环的值,done是一个布尔值,表示循环有没有结束。

console.log([][Symbol.iterator]); // ƒ values() { [native code] }
console.log(new Set()[Symbol.iterator]); // ƒ values() { [native code] }
console.log(new Map()[Symbol.iterator]); // ƒ values() { [native code] }
console.log({}[Symbol.iterator]); // undefined

const it = [1,2][Symbol.iterator]()
it.next() // {value: 1, done: false}
it.next() // {value: 2, done: false}
it.next() // {value: undefined, done: true}

所以不难看出 Object不能使用 for…of遍历是因为没有部署 iterator接口,当给 Object加上 iterator接口时。

const obj = {
  a: 1,
  b: 2
};

obj[Symbol.iterator] = () => {
  let i = 0;
  const arr = Object.entries(obj);

  return {
    next() {
      return i < arr.length
        ? {
            value: arr[i++],
            done: false
          }
        : {
            value: undefined,
            done: true
          };
    }
  };
};

for (const i of obj) {
  console.log(i); // [ 'a', 1 ], [ 'b', 2 ]
}

居然可以遍历了!!!for…of遍历对象居然没报错!!!
但每次都写这么长的迭代器属实是太麻烦了,有没有简单点的方法呢?

const obj = {
  a: 1,
  b: 2
};

obj[Symbol.iterator] = () => Object.entries(obj)[Symbol.iterator]();

这下确实简洁了许多,甚至比 Generator函数返回 Iterator接口还简洁。

Generator

Generator函数与普通函数不同,function关键字与函数名之间有一个星号(*),函数体内部使用yield表达式,定义不同的内部状态。调用时会返回一个指针对象。

function* gen() {
  yield 1;
  yield 2;
}

const it = gen(); // Object [Generator] {}
it.next(); // {value: 1, done: false}
it.next(); // {value: 2, done: false}
it.next(); // {value: undefined, done: true}

所以上述给 Object部署 Iterator接口用 Generator函数可以写成。

const obj = {
  a: 1,
  b: 2
};

obj[Symbol.iterator] = function* () {
  let i = 0;
  const arr = Object.entries(obj);

  while (i < arr.length) {
    yield arr[i++];
  }
};

还是太麻烦了,想再简洁一点就不得不提到 yield* 表达式了。
yield* 表达式可以用来在一个 Generator函数里面执行另一个 Generator函数。
如果yield*后面跟着一个数组,由于数组原生支持遍历器,因此就会遍历数组成员。

所以上述代码可以再简化为。

const obj = {
  a: 1,
  b: 2
};

obj[Symbol.iterator] = function* () {
  yield* Object.entries(obj);
};

所以想使用 for…of 遍历对象时,可以在代码里加上以下两段代码中的任意一段,这样就能轻松的遍历对象了

Object.prototype[Symbol.iterator] = function () {
  return Object.entries(this)[Symbol.iterator]();
};
// or
Object.prototype[Symbol.iterator] = function* () {
  yield* Object.entries(this);
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值