js生成器,迭代器和可迭代对象详解

1.生成器函数和生成器

生成器函数是可以返回一个可迭代对象的特殊函数,

生成器是一个特殊的迭代器,

在js中可以使用function*来定义一个非连续执行的函数作为迭代算法,

function* name() {
   yield value;
   yield value;
   yield value;
}

name: 函数名
value:迭代的值
  • function*:定义一个生成器函数
  • yield:定义迭代对象的值

         最初调用生成器函数时,生成器函数不执行任何代码,而是返回一种称为生成器特殊迭代器。通过调用 next() 方法消耗该生成器时,生成器函数将执行,直至遇到 yield 关键字。

 迭代遍历一个生成器

// 生成器函数
function* generateNumbers() {
  yield 1;
  yield 2;
  yield 3;
  for (let i = 4; i < 10; i++) {
    yield i;
  }
}

// 生成器
let iterator = generateNumbers();

while (true) {
  let { value, done } = iterator.next();
  if (done) {
    break;
  }
  console.log(value);
}

console.log(iterator);

成功将生成器中yield的值全部迭代遍历出来,这是使用了迭代器的next()方法,而生成器是一个特殊的迭代器,

2.迭代器

在 JavaScript 中,迭代器(iterator)是一个对象,它拥有一个next方法,next方法会返回一个对象,对象中包括了两个属性:done,value

  • done: 当迭代器对象中没有值可以跌打时为true,否则为false
  • value: 当前迭代的值,

 迭代器通过调用next方法向后读取值,并返回到value属性中,这样就实现了迭代算法逻辑

迭代器的特性

从结果上看,迭代器和数组的作用差不多,都能遍历一串数据,但是和数组不同,

迭代器往往是不知道长度的,迭代遍历时,只有done属性为true时才会停止,

数组是已知长度的,当一个数组被引用时,内存中就需要划分出足够的空间来存放,

而迭代器只需要提供当前值的空间即可,所以一个数组时有限长度的,而一个迭代器的值可以取0 和 infinity(无穷大) 之间的整数范围

3.可迭代对象

在实际的运用中,可迭代对象发挥了迭代器的特性,可以用来遍历一组数据,这组数据可以非常巨大(长度),使用迭代器则可以很轻松的慢慢读出数据(用实际换空间),

在js中,可迭代对象都拥有一个[Symbol.iterator]属性,它是一个函数,返回一个迭代器,迭代器中包括了可迭代对象的所有值,

常见的可迭代对象

包括了:数组,集合,映射,弱集合,弱映射,字符串  以及其他一些类数组对象(domlist,argument...)

// 可迭代的对象 数组,集合,映射,弱集合,弱映射,字符串  以及其他类数组对象
let arr = new Array(1, 2, 3, 4, 5);
let set = new Set([1, 2, 3, 2, 1, 2]);
let map = new Map([['name', 'John'], [{ type: 'obj' }, 'object']])
let wSet = new WeakSet([{ name: 'John' }, { age: 30 }]);
let wMap = new WeakMap([[{ name: 'John' }, 'person'], [{ age: 30 }, 'person']])
let str = new String('Hello World')

let div = document.querySelectorAll('div');
function fn() {
  return arguments;
}

// 它们原型对象上都存在[symbol.iterator]()方法, arr[Symbol.iterator]().next()
fn()[Symbol.iterator]().next().value

它们原型对象上都存在[symbol.iterator]()方法,  例如,arr[Symbol.iterator]().next(),

[symbol.iterator]是一个Symbol值,关于Symbol可以参考:js基本类型---symbol标识符_jis中symbol是什么意思-CSDN博客

symbol类型是独一无二的,使用symbol作为原型属性,可以防止属性被覆盖修改

注意:普通对象不是可迭代对象,它没有[symbol.iterator]()方法

扩展运算符(...)和for of

扩展运算符可以展开一个数组,并把值全部返回出来,

for of可以遍历数组的值

这些都是基于可迭代对象的[symbol.iterator]()方法,也就是说,其他可迭代对象也可以使用扩展运算符和for of,这也是普通对象不能使用for of的原因,普通对象不是一个可迭代对象

console.log(...arr)
console.log(...set)
console.log(...map)
console.log(...fn(1,2))

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

for(let value of set){
  console.log(value);
}

for(let value of map){
  console.log(value);
}

for(let value of fn(1,2)){
  console.log(value);
}

重写[symbol.iterator]()方法

既然判断一个可迭代对象的方式是看这个对象有没有[symbol.iterator]()方法,那么可以给普通对象加上一个[symbol.iterator]()方法,使其成为一个可迭代对象

let obj = {
  value: 0,
  max: 10,
  [Symbol.iterator]() {
    return {
      max: this.max,  
      value: this.value,
      next() {
        if (this.value == this.max) {
          return {
            value: undefined,
            done: true
          }
        } else {
          return {
            value: this.value++,
            done: false
          }
        }
      }
    }
  }
}

根据[symbol.iterator]()的逻辑,它会返回一个对象,这个对象有next方法,next方法会返回一个包含value和done属性的对象,

那么这个obj对象就相当于一个存放了0到9的一个可迭代对象


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

console.log(...obj);

那么这样就可以对它执行for of 遍历和扩展运算

所以扩展运算符(...)和for of都是基于[symbol.iterator]()方法实现的,

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值