总结

  • for...infor(let k in A) { ... }

    • 数组遍历只能获得键名(0,1,2,...)
    • 能够遍历普通对象的键名
    • 只能用来遍历对象、数组、字符串
    • 字符串遍历和数组相同只能获得键名
    • 会遍历手动添加的属性和原型继承来的属性
    • 能够退出循环,for循环中continue退出当次循环,break退出之后所有循环。注释:return是不能直接和for配合使用的,只能在函数中打断函数内for循环继续执行
  • for...of
    • 数组遍历只能获得键值
    • 不能够遍历普通对象,但是ES6提供了entries、keys、values、Array.from()返回遍历器对象
    • 能遍历以下数据结构
    • 字符串遍历中能够正确识别 32 位 UTF-16 字符,即把'a\uD83D\uDC0A'这样的字符串识别为a普通字符串和\uD83D\uDC0A32 位 UTF-16 字符
    • 只会根据 Iterator 接口返回遍历,数组手动添加属性或在原型上添加属性都不会被遍历
    • 一样能够退出循环
  • forEach等其他遍历方法
    • 不能够退出循环
  • 原生具备 Iterator 接口的数据结构如下
    • Array
    • Map(注释:二维数组,可以视为有顺序的对象使用)
    • Set
    • String
    • TypedArray(注释:指一类数据结构,类型化数组)
    • 函数的 arguments 对象
    • NodeList 对象(注释:一个节点的集合,常通过document.querySelectorAll获得)
  • 调用 Iterator 接口的场合
    • 解构赋值let [x,y] = set;
    • 扩展运算符['a', ...arr, 'd']
    • yield*yield* [2,3,4];
    • for...of
    • Array.from() 注释:从一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例
    • Map(), Set() (比如new Map([['a',1],['b',2]]))注释:Map二维数组,一个数组存键(可以为对象),一个数组存值。先查找键然后通过键的索引查找值。
    • WeakMap(), WeakSet() 注释:相对Map和Set而言,弱引用不会影响垃圾回收
    • Promise.all()
    • Promise.race() 注释:一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。
  • iterator 遍历器实现
    • 对于类似数组的对象(存在数值键名和length属性),部署 Iterator 接口,有一个简便方法,就是Symbol.iterator方法直接引用数组的 Iterator 接口
    • 如果for...of循环提前退出(通常是因为出错,或者有break语句),就会调用return方法。return方法的使用场合是,一个对象在完成遍历前,需要清理或释放资源,就可以部署return方法。
    • next方法是必须部署的,return方法和throw方法是否部署是可选的
class LikeArr {
constructor() {
this.arr = arguments;
}
[Symbol.iterator]() {
let index = 0;
const arr = this.arr;
return {
next() {
return {
value: arr[index++],
done: index > arr.length
};
}
};
}
}
  • 生成器的实现(只用于遍历一次,性能更好)
class RangeIterator {
constructor(start, stop) {
this.value = start;
this.stop = stop;
} [Symbol.iterator]() { return this; } next() {
var value = this.value;
if (value < this.stop) {
this.value++;
return {done: false, value: value};
}
return {done: true, value: undefined};
}
}

————————————————————————————————————————————————————————————————————————————————

https://es6.ruanyifeng.com/#docs/iterator

Iterator(遍历器)的概念

  • Iterator 接口主要供for...of消费
  • Iterator 的遍历过程是:
    • 创建一个指针对象,指向当前数据结构的起始位置。
    • 第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员
    • 不断调用指针对象的next方法,直到它指向数据结构的结束位置。
    • 疑问:指针对象是指创建一个包含内存地址的对象吗?应该是一个普通对象包含value和done两个属性
  • 每一次调用next方法,都会返回包含value和done两个属性的对象
    • value属性是当前成员的值,最后一个可以省略
    • done属性是一个布尔值,表示遍历是否结束。非最后一个时可以省略
    • 注释:最后返回的指针对象为{value:undefined,done:true}
  • 疑问:任何语法结构实际都是函数的一种表现方式?
  • 使用 TypeScript 的写法,遍历器接口(Iterable)、指针对象(Iterator)和next方法返回值的规格可以描述如下(略)

默认 Iterator 接口

  • 当使用for...of循环遍历某种数据结构时,该循环会自动去寻找 Iterator 接口。
  • 默认的 Iterator 接口部署在数据结构的Symbol.iterator属性
    • 注释:done不一定需要返回布尔值,只需要判定为true时遍历便会停止
    • 注释:最后一个指针对象是不会被遍历的,应该是先判断的done的真假
  • 注释:使用class创建构造函数时,constructor中this定义的属性为当前实例的属性,其他地方定义的为其原型上的属性
// 方法1
class LikeArr {
constructor() {
const arr = arguments;
this[Symbol.iterator] = function() {
let index = 0;
return {
next() {
return {
value: arr[index++],
done: index > arr.length
};
}
};
};
}
}
// 方法2
class LikeArr {
constructor() {
this.arr = arguments;
}
[Symbol.iterator]() {
let index = 0;
const arr = this.arr;
return {
next() {
return {
value: arr[index++],
done: index > arr.length
};
}
};
}
}
// 方法3
function LikeArr() {
this.arr = arguments;
}
LikeArr.prototype[Symbol.iterator] = function() {
let index = 0;
const arr = this.arr;
return {
next() {
return {
value: arr[index++],
done: index > arr.length
};
}
};
}; const likeArr = new LikeArr(1, 3, 5, 6, 7);
  • 注释:for...of循环用函数表示
for (const iterator of likeArr) {
console.log(iterator);
} function forFun(arr) {
const iteratorObj = arr[Symbol.iterator]();
const toNext = function(iteratorObj) {
const iterator = iteratorObj.next();
if (iterator.done) return;
console.log(iterator.value);
toNext(iteratorObj);
};
toNext(iteratorObj);
}
forFun(likeArr);
  • 原生具备 Iterator 接口的数据结构如下

    • Array
    • Map(注释:二维数组,可以视为有顺序的对象使用)
    • Set
    • String
    • TypedArray(注释:指一类数据结构,类型化数组)
    • 函数的 arguments 对象
    • NodeList 对象(注释:一个节点的集合,常通过document.querySelectorAll获得)
  • 对象(Object)之所以没有默认部署 Iterator 接口,是因为对象的哪个属性先遍历,哪个属性后遍历是不确定的,需要开发者手动指定。
    • 疑问:Set是有顺序的?
  • 注释:创建一个具有Iterator的类,注意this的用法
  • 注释:这是一个生成器?适用于需要遍历每一项的大数据,用以节省空间。
  • 注释:该class只能遍历一次,源于它特别的Symbol.iterator返回,而且在遍历时更省内存
class RangeIterator {
constructor(start, stop) {
this.value = start;
this.stop = stop;
} [Symbol.iterator]() { return this; } next() {
var value = this.value;
if (value < this.stop) {
this.value++;
return {done: false, value: value};
}
return {done: true, value: undefined};
}
} function range(start, stop) {
return new RangeIterator(start, stop);
} for (var value of range(0, 3)) {
console.log(value); // 0, 1, 2
}
  • 通过遍历器实现指针结构的例子
  • 注释:current = current.next;实现当前指针向下一个对象移动
  • 注释:可以把遍历过程理解为对当前实例next方法的调用结果,而这个当前对象是可以变动的
function Obj(value) {
this.value = value;
this.next = null;
} Obj.prototype[Symbol.iterator] = function() {
var iterator = { next: next }; var current = this; function next() {
if (current) {
var value = current.value;
current = current.next;
return { done: false, value: value };
} else {
return { done: true };
}
}
return iterator;
} var one = new Obj(1);
var two = new Obj(2);
var three = new Obj(3); one.next = two;
two.next = three; for (var i of one){
console.log(i); // 1, 2, 3
}
  • 对于类似数组的对象(存在数值键名和length属性),,部署 Iterator 接口,有一个简便方法,就是Symbol.iterator方法直接引用数组的 Iterator 接口

    • 注释:这里的3:'d'是不会遍历到的,受制于length。同理其他属性也不会被遍历
let iterable = {
0: 'a',
1: 'b',
2: 'c',
3: "d",
length: 3,
[Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let item of iterable) {
console.log(item); // 'a', 'b', 'c'
}

调用 Iterator 接口的场合

(1)解构赋值

  • 对数组和 Set 结构进行解构赋值时,会默认调用Symbol.iterator方法
let set = new Set().add('a').add('b').add('c');

let [x,y] = set;
// x='a'; y='b' let [first, ...rest] = set;
// first='a'; rest=['b','c'];

(2)扩展运算符

  • 提供了一种简便机制,可以将任何部署了 Iterator 接口的数据结构,转为数组
// 例一
var str = 'hello';
[...str] // ['h','e','l','l','o'] // 例二
let arr = ['b', 'c'];
['a', ...arr, 'd']
// ['a', 'b', 'c', 'd']

(3)yield*

  • yield*后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。

    • 疑问:需要对Generator进行深入了解
  • 注释:这里讲的是yield* [2,3,4]在for...of遍历时作为已扩展对象,即遍历了1后,依次遍历2,3,4,而不是把[2,3,4]作为一个遍历对象
  • 注释:generator可以作为一个Symbol.iterator使用
let generator = function* () {
yield 1;
yield* [2,3,4];
yield 5;
}; var iterator = generator(); iterator.next() // { value: 1, done: false }
iterator.next() // { value: 2, done: false }
iterator.next() // { value: 3, done: false }
iterator.next() // { value: 4, done: false }
iterator.next() // { value: 5, done: false }
iterator.next() // { value: undefined, done: true }

(4)其他场合

  • 由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其实都调用了遍历器接口。

    • for...of
    • Array.from() 注释:从一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例
    • Map(), Set() (比如new Map([['a',1],['b',2]]))注释:Map二维数组,一个数组存键(可以为对象),一个数组存值。先查找键然后通过键的索引查找值。
    • WeakMap(), WeakSet() 注释:相对Map和Set而言,弱引用不会影响垃圾回收
    • Promise.all()
    • Promise.race() 注释:一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。

字符串的 Iterator 接口

  • 可以覆盖原生的Symbol.iterator方法,达到修改遍历器行为的目的

Iterator 接口与 Generator 函数

  • Symbol.iterator方法的最简单实现,还是使用下一章要介绍的 Generator 函数

    • 注释:Generator函数返回一个含有next方法的对象
let myIterable = {
[Symbol.iterator]: function* () {
yield 1;
yield 2;
yield 3;
}
}
[...myIterable] // [1, 2, 3] // 或者采用下面的简洁写法 let obj = {
* [Symbol.iterator]() {
yield 'hello';
yield 'world';
}
}; for (let x of obj) {
console.log(x);
}
// "hello"
// "world"

遍历器对象的 return(),throw()

  • next方法是必须部署的,return方法和throw方法是否部署是可选的
  • 如果for...of循环提前退出(通常是因为出错,或者有break语句),就会调用return方法。return方法的使用场合是,一个对象在完成遍历前,需要清理或释放资源,就可以部署return方法。
  • return方法必须返回一个对象,这是 Generator 规格决定的
    • 注释:chrome 80.0.3987.116 return方法并不一定需要返回
function readLinesSync(file) {
return {
[Symbol.iterator]() {
return {
next() {
return { done: false };
},
return() {
file.close();
return { done: true };
}
};
},
};
} // 会在执行return方法关闭文件之后,再抛出错误
for (let line of readLinesSync(fileName)) {
console.log(line);
throw new Error();
}

for...of 循环

  • 注释:Generator对象可以被forof遍历

数组

  • JavaScript 原有的for...in循环,只能获得对象的键名,不能直接获取键值。ES6 提供for...of循环,允许遍历获得键值。
  • for...of循环调用遍历器接口,数组的遍历器接口只返回具有数字索引的属性。这一点跟for...in循环也不一样。
let arr = [3, 5, 7];
arr.foo = 'hello'; for (let i in arr) {
console.log(i); // "0", "1", "2", "foo"
} for (let i of arr) {
console.log(i); // "3", "5", "7"
}

Set 和 Map 结构

  • Map进行for...of循环时,遍历的value是一个长度为2的数组,第一位为key第二位为val
var es6 = new Map();
for (var [name, value] of es6) {
console.log(name + ": " + value);
}

计算生成的数据结构

  • entries() 返回一个遍历器对象,用来遍历[键名, 键值]组成的数组。对于数组,键名就是索引值;对于 Set,键名与键值相同。Map 结构的 Iterator 接口,默认就是调用entries方法。
  • keys() 返回一个遍历器对象,用来遍历所有的键名。注释:set返回键值组成的数组
  • values() 返回一个遍历器对象,用来遍历所有的键值。

类似数组的对象

  • for...of循环还有一个特点,就是会正确识别 32 位 UTF-16 字符。注释:这里\uD83D\uDC0A是一个32的 UTF-16 字符
for (let x of 'a\uD83D\uDC0A') {
console.log(x);
}
  • Array.from() 可以通过以下方式来创建数组对象

    • 伪数组对象(拥有一个 length 属性和若干索引属性的任意对象)
    • 可迭代对象(可以获取对象中的元素,如 Map和 Set 等)
let arrayLike = { length: 2, 0: 'a', 1: 'b' };

// 报错
for (let x of arrayLike) {
console.log(x);
} // 正确
for (let x of Array.from(arrayLike)) {
console.log(x);
}

对象

  • 对于普通的对象,for...in循环依然可以用来遍历键名。

与其他遍历语法的比较

  • 数组无法中途跳出forEach循环,break命令或return命令都不能奏效。
  • for...in循环不仅遍历数字键名,还会遍历手动添加的其他键,甚至包括原型链上的键。
  • 某些情况下,for...in循环会以任意顺序遍历键名。
    • 疑问:对于对象?
  • for...in循环主要是为遍历对象而设计的,不适用于遍历数组
  • for循环中continue退出当次循环,break退出之后所有循环。注释:return是不能直接和for配合使用的,只能在函数中打断函数内for循环继续执行

疑问:Iterator 遍历器和数据集合各种遍历方法的区别的更多相关文章

  1. Java SE之For增强与Iterator遍历器提取数据(附Map.Entry)

    增强for循环: 1.操作数组 2.操作List集合 3.操作Map集合    1.map.values()法    2.map.keySet()法  [传统方法]    3.Map.Entry法   ...

  2. C#遍历DataSet中数据的几种方法总结

    //多表多行多列的情况foreach (DataTable dt in YourDataset.Tables) //遍历所有的datatable{foreach (DataRow dr in dt.R ...

  3. js遍历map匹配数据和js遍历数组匹配map数据

    var __LocalDataCities = { list: { "010": ["北京", "BEIJING"], "0100 ...

  4. 【前端】【javascript】es6中的遍历器接口Iterator

    好久没发文章啦-.-为了证明我还活着,我决定从笔记里面抓一篇还算不乱比较像文章的发出来... 这些笔记是我在学es6的时候断断续续记录的,最近会一份一份整理陆陆续续发出来,顺便也自己再看一遍.我学习e ...

  5. Iterator(遍历器) 和 for...of 循环

    是generator的前置知识 generator :https://www.cnblogs.com/wangtong111/p/11322961.html 遍历器(Iterator)就是这样一种机制 ...

  6. ES6的 Iterator 遍历器到底是什么?

    这节课要讲的是ES6中的Iterator. for...of为啥不遍历Object对象 第十三节我们讲了简单又实用的for...of,我们可以使用它来遍历数组,字符串,Set和Map结构,但是有没有发 ...

  7. Iterator 遍历器

    1.遍历器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制.任何数据结构只要部署Iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员). 2.Iterator ...

  8. ES6学习笔记(十三)Iterator遍历器和for...of循环

    1.概念 遍历器(Iterator)就是这样一种机制.它是一种接口,为各种不同的数据结构提供统一的访问机制.任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有 ...

  9. 两种QMultiMap的遍历方法(最好使用只读遍历器)

    留个爪,备查 QMultiMap<QString, QString>& remote_map = my_obj->m_MapVersion; // ccc 这里体现了引用的好 ...

随机推荐

  1. 03讲基础篇:经常说的CPU上下文切换是什么意思(上)

    小结 总结一下,不管是哪种场景导致的上下文切换,你都应该知道: CPU 上下文切换,是保证 Linux 系统正常工作的核心功能之一,一般情况下不需要我们特别关注. 但过多的上下文切换,会把CPU时间消 ...

  2. 使用Gradle自动创建Java项目结构

    D:\N3verL4nd\Desktop\java>tree 卷 本地磁盘 的文件夹 PATH 列表 卷序列号为 00000200 0006:08B0 D:. ├─.gradle │ ├─3.4 ...

  3. Codeforces_812

    A. 每条人行道有六条车道会撞到. #include<bits/stdc++.h> using namespace std; ],b[],c[],d[]; int main() { ios ...

  4. 2.5D(伪3D)站点可视化第一弹

    楔子 最近要做一个基站站点的可视化呈现项目. 我们首先尝试的是三维的可视化技术来程序,但是客户反馈的情况是他们的客户端电脑比较差,性能效率都会不好,甚至有的还是云主机. 因此我们先做了一个性能比较极致 ...

  5. Entity Framework 实体状态

    从今天开始我们开始讲解EF中的实体状态和数据操作,这篇文章先讲解实体状态. 我们通过前面的学习,知道EF通过上下位负责跟踪实体的状态,实体状态的位置是在命名空间 System.Dat.Entity 里 ...

  6. OOP(面向对象编程)

    原文链接:https://blog.csdn.net/qq_38526635/article/details/81319127OOP编程思想OOP核心思想:封装,继承,多态. 理解: 对象是由数据和容 ...

  7. Window10和Ubuntu 18.04双系统安装的引导问题解决

    作为码农 首先,建议了解下grub2的启动顺序和逻辑.可以参考这篇文章,grub.cfg详解. 从执行顺序倒推,如下如果全部执行成功,则会进入grub的启动菜单:如果最后一步,没有找到grub.cfg ...

  8. 使用matplotlib画图

    一.介绍 官方文档:https://www.matplotlib.org.cn/home.html 安装:pip install matplotlib Matplotlib是一个Python 2D绘图 ...

  9. 动态规划------背包问题(c语言)

    /*背包问题: 背包所能容纳重量为10:共五件商品,商品重量用数组m存储m[5]={2,2,6,5,4}, 每件商品的价值用数组n存储,n[5]={6,3,5,4,6};求背包所能装物品的最大价值. ...

  10. Django使用 djcelery时报ImportError: No module named south.db错误

    这时候可能是安装的Django-celery.celery的版本过低引起的,可以到pycharm查看推荐的版本,把版本更换到的推荐的版本就解决了