疑问:Iterator 遍历器和数据集合各种遍历方法的区别
总结
- for...in
for(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\uDC0A
32 位 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 遍历器和数据集合各种遍历方法的区别的更多相关文章
- Java SE之For增强与Iterator遍历器提取数据(附Map.Entry)
增强for循环: 1.操作数组 2.操作List集合 3.操作Map集合 1.map.values()法 2.map.keySet()法 [传统方法] 3.Map.Entry法 ...
- C#遍历DataSet中数据的几种方法总结
//多表多行多列的情况foreach (DataTable dt in YourDataset.Tables) //遍历所有的datatable{foreach (DataRow dr in dt.R ...
- js遍历map匹配数据和js遍历数组匹配map数据
var __LocalDataCities = { list: { "010": ["北京", "BEIJING"], "0100 ...
- 【前端】【javascript】es6中的遍历器接口Iterator
好久没发文章啦-.-为了证明我还活着,我决定从笔记里面抓一篇还算不乱比较像文章的发出来... 这些笔记是我在学es6的时候断断续续记录的,最近会一份一份整理陆陆续续发出来,顺便也自己再看一遍.我学习e ...
- Iterator(遍历器) 和 for...of 循环
是generator的前置知识 generator :https://www.cnblogs.com/wangtong111/p/11322961.html 遍历器(Iterator)就是这样一种机制 ...
- ES6的 Iterator 遍历器到底是什么?
这节课要讲的是ES6中的Iterator. for...of为啥不遍历Object对象 第十三节我们讲了简单又实用的for...of,我们可以使用它来遍历数组,字符串,Set和Map结构,但是有没有发 ...
- Iterator 遍历器
1.遍历器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制.任何数据结构只要部署Iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员). 2.Iterator ...
- ES6学习笔记(十三)Iterator遍历器和for...of循环
1.概念 遍历器(Iterator)就是这样一种机制.它是一种接口,为各种不同的数据结构提供统一的访问机制.任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有 ...
- 两种QMultiMap的遍历方法(最好使用只读遍历器)
留个爪,备查 QMultiMap<QString, QString>& remote_map = my_obj->m_MapVersion; // ccc 这里体现了引用的好 ...
随机推荐
- 03讲基础篇:经常说的CPU上下文切换是什么意思(上)
小结 总结一下,不管是哪种场景导致的上下文切换,你都应该知道: CPU 上下文切换,是保证 Linux 系统正常工作的核心功能之一,一般情况下不需要我们特别关注. 但过多的上下文切换,会把CPU时间消 ...
- 使用Gradle自动创建Java项目结构
D:\N3verL4nd\Desktop\java>tree 卷 本地磁盘 的文件夹 PATH 列表 卷序列号为 00000200 0006:08B0 D:. ├─.gradle │ ├─3.4 ...
- Codeforces_812
A. 每条人行道有六条车道会撞到. #include<bits/stdc++.h> using namespace std; ],b[],c[],d[]; int main() { ios ...
- 2.5D(伪3D)站点可视化第一弹
楔子 最近要做一个基站站点的可视化呈现项目. 我们首先尝试的是三维的可视化技术来程序,但是客户反馈的情况是他们的客户端电脑比较差,性能效率都会不好,甚至有的还是云主机. 因此我们先做了一个性能比较极致 ...
- Entity Framework 实体状态
从今天开始我们开始讲解EF中的实体状态和数据操作,这篇文章先讲解实体状态. 我们通过前面的学习,知道EF通过上下位负责跟踪实体的状态,实体状态的位置是在命名空间 System.Dat.Entity 里 ...
- OOP(面向对象编程)
原文链接:https://blog.csdn.net/qq_38526635/article/details/81319127OOP编程思想OOP核心思想:封装,继承,多态. 理解: 对象是由数据和容 ...
- Window10和Ubuntu 18.04双系统安装的引导问题解决
作为码农 首先,建议了解下grub2的启动顺序和逻辑.可以参考这篇文章,grub.cfg详解. 从执行顺序倒推,如下如果全部执行成功,则会进入grub的启动菜单:如果最后一步,没有找到grub.cfg ...
- 使用matplotlib画图
一.介绍 官方文档:https://www.matplotlib.org.cn/home.html 安装:pip install matplotlib Matplotlib是一个Python 2D绘图 ...
- 动态规划------背包问题(c语言)
/*背包问题: 背包所能容纳重量为10:共五件商品,商品重量用数组m存储m[5]={2,2,6,5,4}, 每件商品的价值用数组n存储,n[5]={6,3,5,4,6};求背包所能装物品的最大价值. ...
- Django使用 djcelery时报ImportError: No module named south.db错误
这时候可能是安装的Django-celery.celery的版本过低引起的,可以到pycharm查看推荐的版本,把版本更换到的推荐的版本就解决了