[JS]带你掌握Iterator迭代器

转:https://juejin.cn/post/7055958299996323854#heading-0

image.png

前言

【Iterator】(迭代器)是ES6中JavaScript的一个新特性,拥有迭代器特性的数据,js可以为它执行如for...of,map(),filter()等迭代方法。掌握这个技巧之后,在某些开发业务常见中,我们可以利用迭代器做出更合适的技术选择。

迭代协议

值得注意的是,在ES6中我们看到围绕着迭代器,新增出了许多的API。如for...of,map(),filter()等。可是这些方法的实现,并不是ES在底层新增了一些内置实现或者语法。ES做的只是定制了一套【迭代协议】。

这个迭代协议有2个部分,【可迭代协议】和【迭代器协议】。

迭代器协议

迭代器协议规定了,一个迭代器应该要长什么样子。在官方规定中,一个对象想要被称为“迭代器”,就必须拥有next方法。而这个next方法调用之后会返回一个对象。这个对象有2个属性value和done。

  • value 是本次迭代的返回值。在done为true之后,value就没有意义了,但最好返回undefined。
  • done 是迭代结束的标识。

根据这个规范,我们大致可以写出一个迭代器应该是以下的样子:

var myIterator = {
  next:function(){
    count++;
    if(count<3){
      return {value: count,done:false};
    }else{
      return {value: undefined,done:true};
    }    
    },
  count:0
}
复制代码

可迭代协议

可是光有迭代器是没用的,我们需要有一个载体能让迭代器跑起来。这就是【可迭代协议】的工作了。可迭代协议规范了哪些数据可以作为运行迭代器的载体。在JavaScript中,String、Array、TypedArray、Map 和 Set 等都是内置的符合可迭代协议的类型。

其中最常见的调用迭代的方式之一就是for...of。

for(var i of [1,2,3]){
    console.log(i);
}

for(var i of "123"){
    console.log(i);
}

for(var i of new Set([1,2,3])){
    console.log(i);
}
// 以上均输出
// 1
// 2
// 3
复制代码

Symbol.iterator

ok,现在我们知道了平常的一些类型可以调用迭代方法是因为他们符合了可迭代协议。可是这个协议具体是什么?我们可以手动的让其他类型也符合这个协议吗?

答案其实是 @@iterator 方法,只有一个数据身上有 @@iterator 方法就符合可迭代器协议了。而这个方法可以通过被【Symbol.iterator】属性引用。

为此我们可以在浏览器控制台输出array,map,set等类型,可以看到他们都内置了一个Symbol.iterator属性。

[图片上传失败...(image-e345ea-1658818947596)]

因此我们可以简单地理解为只有一个数据(不管是什么内类),需要它身上有【Symbol.iterator】属性,且这个属性指向一个符合迭代器协议的迭代器之后。这个数据就符合了可迭代协议。

手动实现Iterator

读到这里,其实我们已经做好了所有前置准备了。现在我们开始来手动为一个对象(非内置符合可迭代器协议的数据类型)实现for of 方法。

// 在js中对象类型,默认并不符合可迭代协议
var obj = {a:'a',b:'b'};
for(var i of obj){
    console.log(i);
}
// 执行会报错  Uncaught TypeError: obj is not iterable
复制代码

我们先写一个迭起器。

// 根据迭代器协议,写一个迭代器
var myIterator = function(){
  var done = false;
  return {
    next: function(){
      if(done){
        return {value:'hi',done:true};
      }else{
        done = true;
        return {value:'going',done:false};
      }
    }
  }
}
复制代码

然后给一个对象添加【Symbol.iterator】属性,并指向上面的写的迭代器。

var obj={};
obj[Symbol.iterator] = myIterator;
复制代码

此时再调用for...of

for(var i of obj){
    console.log(i);
}
// 正常输出
// 'going'
复制代码

可以看到,obj已经可以像内置符合迭代器的类型一样执行for...of了。

结合生成器优化写法

相信细心的朋友在看到next方法时,就想到了生成器。既然迭代是在不停地调用next方法,生成器函数又天生自带next方法,那么是不是可以直接用生成器来定义迭代器呢?答案是可以的,结合生成器我们可以写出更加简练的代码。

var myIterator = function*(){
    yield 11;
    yield 12;
    yield 13;
    yield 14;
    yield 15;
}

// 定义一个空的数组
var arr = [];
arr[Symbol.iterator] = myIterator;
for(var i of arr){
    console.log(i);
}
// 输出
// 11
// 12
// 13
// 14
// 15
复制代码

所以生成器究竟是对象还是函数?

通过上述的简化实现之后,我们发现结合生成器确实能实现我们想要的效果。可是本文前面不是说了,迭代器必须符合迭代器协议吗?迭代器不是应该是一个对象吗?为什么这里生成器是一个函数也符合这个协议?

其实大家不用怀疑,因为生成器他既是一个函数也是一个对象。

// 定义一个生成器
let aGeneratorObject = function* (){
    yield 1;
    yield 2;
    yield 3;
}();

typeof aGeneratorObject.next;
// 返回"function", 他有一个next方法,说明他符合迭代器协议

typeof aGeneratorObject[Symbol.iterator];
// 返回"function", 有一个@@iterator方法,说明生成器本身也是可迭代对象

aGeneratorObject[Symbol.iterator]() === aGeneratorObject;
// 返回true, 因为@@iterator方法返回自身(即迭代器),说明这是一个格式良好的可迭代对象

[...aGeneratorObject];
// 返回[1, 2, 3]

console.log(Symbol.iterator in aGeneratorObject)
// 返回true, 因为@@iterator方法是aGeneratorObject的一个属性
复制代码

总结

今天我们掌握了JavaScript中的迭代器协议,知道了迭代器与可迭代对象的关系。并手动实现了自定义迭代器的效果。希望可以帮助大家对迭代器有深入的了解,并在开发中运用起来。

参考

developer.mozilla.org/zh-CN/docs/…

developer.mozilla.org/zh-CN/docs/…

developer.mozilla.org/zh-CN/docs/…

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容

  • iterator(迭代)一般很少直接使用, 但是却是很常用很重要的功能.例如 :对象的扩展运算符(...)内部其实...
    攻城熊阅读 2,102评论 0 0
  • 迭代的英文是 “iteration”,源自拉丁文 “itero”,是“重复”或“再来”的意思。在软件开发领域,“迭...
    越前君阅读 945评论 0 3
  • 前言 很多同学在第一次听到生成器这个概念的时候,总觉得是前端高大上的东西,可能现在依然有很多前端同学不理解这个概念...
    Weastsea阅读 318评论 0 0
  • 迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常...
    BenBonBen阅读 317评论 0 0
  • 一、Iterator 1.Iterator(迭代器)的概念 遍历器(Iterator) 是一种用来处理所有不同的数...
    时光已翩然轻擦阅读 245评论 0 2