摸鱼日记之—— js 中的遍历器:Iterator 与 for of

1.什么是Iterator ?


遍历器(Iterator)它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。

Iterator 的作用有三个:

1.是为各种数据结构,提供一个统一的、简便的访问接口;

2.是使得数据结构的成员能够按某种次序排列;

3.是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费。

2.Iterator怎么用 ?


for...of语句可迭代对象(包括 ArrayMapSetStringTypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句

/**
 * 语法
 * @variable 每次迭代中属性的值
 * @iterable 被迭代枚举其属性的对象
 */
for (variable of iterable) {
    //statements
}
const array1 = ['a', 'b', 'c'];

for (const element of array1) {
  console.log(element);
}

// a
// b
// c

接下来一起来实践一下上面那些类型是不是真的可以用

// Array
let arr = [10, 20, 30];

for (let value of arr) {
    value += 1;
    console.log(value);
}
// 11
// 21
// 31

// Map
let map = new Map([["a", 1], ["b", 2], ["c", 3]]);

for (let entry of map) {
  console.log(entry);
}
// ["a", 1]
// ["b", 2]
// ["c", 3]

// Set
let set = new Set([1, 1, 2, 2, 3, 3]);

for (let value of set) {
  console.log(value);
}
// 1
// 2
// 3

// String
let str = "boo";

for (let value of str) {
  console.log(value);
}
// "b"
// "o"
// "o"

// TypedArray
let typedArr = new Uint8Array([0x00, 0xff]);

for (let value of typedArr) {
  console.log(value);
}
// 0
// 255

// arguments
(function() {
  for (let argument of arguments) {
    console.log(argument);
  }
})(1, 2, 3);
// 1
// 2
// 3

View Code

那其他的类型用for of会怎么样?

比如一个普通的object类型

产品:“那我就要把for of用在对象上”

我:“对象不能用for of,你看这不都报错了嘛”

产品:“不听不听,你是不是不想做”

我:“好吧好吧,既然你想要用,那就满足你”

首先要了解遍历器 Iterator 的协议,传送门:MDN 迭代协议

简而言之就是:

迭代协议具体分为两个协议:可迭代协议迭代器协议

可迭代协议:

可迭代协议允许 JavaScript 对象定义或定制它们的迭代行为,例如,在一个 for..of 结构中,哪些值可以被遍历到。一些内置类型同时是内置可迭代对象,并且有默认的迭代行为,比如 Array 或者 Map,而其他内置类型则不是(比如 Object))。

要成为可迭代对象, 一个对象必须实现 @@iterator 方法。这意味着对象(或者它原型链上的某个对象)必须有一个键为 @@iterator 的属性,可通过常量 Symbol.iterator 访问该属性:

迭代器协议:

迭代器协议定义了产生一系列值(无论是有限个还是无限个)的标准方式。当值为有限个时,所有的值都被迭代完毕后,则会返回一个默认返回值。

只有实现了一个拥有以下语义(semantic)的 next() 方法,一个对象才能成为迭代器:

var obj = {
  data: [1,2,3],
  [Symbol.iterator]() {
    var nextIndex = 0, self = this;
    return {
      next() {
        var done = nextIndex >= self.data.length;
        var value = done ? undefined : self.data[nextIndex++]
        return { value: value, done: done }
      }
    }
  }
}

for (const item of obj) { console.log(item) }// 1// 2// 3

由此可见,普通对象不可直接使用for of,在[Symbol.iterator]属性上部署遍历器生成的方法后即可被for of使用(原型链上的对象具有该方法也可)

实际上,对象之所以没有默认部署 Iterator 接口,是因为对象的哪个属性先遍历,哪个属性后遍历是不确定的,需要开发者手动指定。

那知道了上面遍历器的协议,我们可以通过更简单的方式判定上述六种类型是否实现了遍历器接口了

Array.prototype.hasOwnProperty(Symbol.iterator);
// true
Set.prototype.hasOwnProperty(Symbol.iterator);
// true
Map.prototype.hasOwnProperty(Symbol.iterator);
// true
String.prototype.hasOwnProperty(Symbol.iterator);
// true
(function(){ console.log(arguments.hasOwnProperty(Symbol.iterator)) })(1,2,3)
// true
Uint8Array.prototype.__proto__.hasOwnProperty(Symbol.iterator)
// true

3. 与for in的区别


可能看到for of 有人想到for in,那这两个长这么像,他两有啥区别呢。咱们一起来看看

var arr1 = ['a', 'b', 'c', 'd'];
// 输出键名
for (let a in arr1) {
  console.log(a); // 0 1 2 3
}
// 输出键值
for (let a of arr1) {
  console.log(a); // a b c d
}

// 注意:还有个细节
// for...of循环调用遍历器接口,数组的遍历器接口只返回具有数字索引的属性。这一点跟for...in循环也不一样。
let arr2 = [3, 5, 7];
arr2.foo = 'hello';

for (let i in arr2) {
  console.log(i); // "0", "1", "2", "foo"
}

for (let i of arr2) {
  console.log(i); //  "3", "5", "7"
}

4. 与其他循环的区别


var arr = Array(10).fill(1);
// for i 循环法,不够简洁
for (var i = 0; i < arr.length; i++) {
  console.log(arr[i])
}

// forEach 循环,不能手动跳出循环
arr.forEach(function (value) {
  console.log(value);
})

// for i 循环获取的是键名,而不是键值,因此不大适用于遍历数组
for (var i in arr) {
  console.log(i)
}
// for of 可以跳出循环,语法简洁
for (var i of arr) {
  console.log(i)
}

经比较,遍历对象用for in, 遍历数组用for of

实际上,要实现对象的Iterator接口还有更简洁的方法,就是使用generator

let obj = {
  * [Symbol.iterator]() {
    yield 'hello';
    yield 'world';
  }
};

for (let x of obj) {
  console.log(x);
}
// hello
// world

看到这里有人又奇怪了,这个 * 号是个什么鬼?

答案是 Generator 函数,Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。

详情请戳这里

2020-06-09 11:23:51

(0)

相关推荐

  • JavaScript-迭代器模式

    迭代器模式 顺序访问一个集合 使用者无需知道集合内部结构(封装) jQuery 示例 <!DOCTYPE html> <html> <head> <meta ...

  • es6新增新特性简要总结

    es6简介 es6是在2015年6月正式颁布的新标准,es6基本上实现了所有ECMAScript 规范,以后每年的6月都会发布新版本,但改动不大. let 变量 使用let 关键字来申明的变量拥有以下 ...

  • 日记382--浑水摸鱼

          端午节的第二天过得开心快乐,大人玩的比小孩还要开心,媳妇在购物狂上搜到的一个小河沟,十几公里驱车1个小时才到达,一是出行高峰在收费站堵了10分钟,二是到达目的地附近时一直找不到地点,兜转耽 ...

  • 年薪百万的谷歌精英:比起加班,我更爱工作中“摸鱼”

    在国内互联网大厂工作是一种什么体验? 当人们谈及这个话题时,除了令人羡慕的高收入,必然还会想到诸如996.超级大小周等诸多高强度的疯狂工作模式. 但如果告诉你,许多国外头部互联网,不仅不鼓励加班,反而 ...

  • 摸鱼也要讲究方法:工作学习中玩手机并不能缓解无聊和疲劳

    智能手机的出现,对社会产生了深远的影响,人们不仅可以用智能手机来拨打电话或发送短信,还用于处理各种应用程序.当代社会,手机早已成为人们生活.学习.工作.娱乐休闲中必不可少的工具. 许多人认为,玩手机能 ...

  • 1938年的北京西直门外,田野中矗立着古塔,孩子们在河沟里摸鱼

    西直门是北京内城的九大古城门之一,早在元朝时期就已经是北京的重要通行关口.在明清两代,西直门是玉泉山往皇宫送水的必经之地,因此又有"水门"之称. 本组图片呈现的是1938年9月西直 ...

  • 学会在工作中“摸鱼”,做事才能事半功倍丨三种“摸鱼”心理解析

    知乎上有人调侃道,爱因斯坦在瑞士工作时,一边审查专利文件,一边"摸鱼"研究相对论:刘慈欣在山西娘子关发电厂工作时,一边做自己工程师的本职工作,一边"摸鱼"写&l ...

  • 摸鱼摸得好,也能反过来剥削老板

    看理想 公众号"看理想",探寻文化生活的另一种可能.05-06 14:07 五一假期结束,上班的第一天,人回到岗位上,心却还流连忘返在假期的美好回忆里,已然将"带薪摸鱼& ...

  • 摸鱼达人的偷懒秘诀,两招word小技巧轻松搞定工作!

    来源:秋叶办公技巧 编辑:萌萌 哈喽,大家好,这里是秋叶编辑部,我是绝不想加班的禾一 . 不知道大家平常在使用 Word 办公时,是否遇到过这种情况: 马上就要下班了,领导突然来到你的工位让你改一下 ...

  • 完美用电脑刷微信朋友圈,以后人人都可以安心摸鱼了!

    相信绝大多数人在办公室,或者平时自己用的电脑,都是Windows系统吧,能用电脑刷朋友圈摸鱼,岂不是爽歪歪? 这次Windows内测版的微信也实现了刷朋友圈的功能,小喵给大家弄来了! 小喵专门找的内测 ...

  • 陈柏衡《摸鱼趣事》指导老师:吴芳芳

    摸鱼趣事 浙江省杭州市临安区晨曦小学四(6)班 陈柏衡 窗外,风打着呼啸,从窗边飞过,点点星光洒在我的床上,我仰着头望着,望着,想起了儿时一件趣事. 那是大约两年前,七八月份,正是夏季最热的时候,我们 ...