JS中如何将一个普通对象转换为可迭代对象,并且可使用for...of迭代对象,内含手写一个简单的迭代器

news2024/11/28 0:43:14

文章目录

  • 一、认识迭代器
  • 二、为类数组添加迭代器方法
  • 三、为colorObj对象添加迭代器方法
  • 四、优化colorObj代码
  • 五、ES6内置迭代对象方法

这里有一个普通对象:

const colorObj = {
	white: "小白",
	black: "小黑",
	gray: "小灰",
}

如何对 colorObj 对象进行for...of迭代获取其中的值呢?

for (const value of colorObj) {
	console.log(value);
}
// Uncaught TypeError: colorObj is not iterable

如果直接使用for...of迭代对象,会像下面这样报错:

在这里插入图片描述


一、认识迭代器

这里就需要具备迭代器 Iterator 的知识了。那么,什么是迭代器呢?我们先来看一下迭代的定义:

迭代就是指可以从一个数据集中按照一定的顺序,不断取出数据的过程。

再来说一说迭代器

在JavaScript中,迭代器是能调用 next 方法实现迭代的一个对象,该方法返回一个具有两个属性的对象。

  • value:可迭代对象的下一个值
  • done:表示是否已经取出所有的数据了。 false表示还有数据, true表示已经取出所有数据
const arr = [1, 2, 3];
let arrIterator = arr[Symbol.iterator]();  // 调用数组的迭代器方法
arrIterator.next();  // 执行迭代器的next方法
--> { value: 1, done: false }

arrIterator.next();
--> { value: 2, done: false }

arrIterator.next();
--> { value: 3, done: false }

arrIterator.next();
--> { value: undefined, done: true }

原生具备迭代器接口的数据结构有:ArrayStringSetMap,特殊类数组对象:arguments、NodeList 等。

此处列举 Set 的 Iterator 接口:

在这里插入图片描述

对象不可迭代,因为它没有指定 Iterator 接口。可以为对象添加迭代方法,对象才可迭代。才可供for…of消费。

在这里插入图片描述

Iterator 的作用有三个:

  1. 为各种数据结构,提供一个统一的、简便的访问接口;
  2. 使得数据结构的成员能够按某种次序排列;
  3. ES6 创造了一种新的遍历命令 for…of 循环,Iterator 接口主要供 for…of 消费。

二、为类数组添加迭代器方法

先来看一个简单的类数组对象:

const arrayLike= {
	0: "小白",
	1: "小黑",
	2: "小灰",
	length: 3
}

如上代码所示,类数组需要满足两个条件:

  1. 具备length属性;
  2. key必须是一组有序的数字;

现在我们想迭代当前的类数组对象 arrayLike,必须为其添加 iterator 接口:

// 添加迭代器方法 Symbol.iterator
arrayLike[Symbol.iterator] = function () {
  let index = 0;
  return {  // 返回一个对象,里面带有next方法,执行next方法,返回一个带有value与done属性的对象
    next: () => {
      if (index < this.length) {  // 判断是否还存在值,存在值则返回值,否则返回 { value: undefined, done: true }
        const result = { value: this[index], done: false };
        index++;  // index自增,查找下一个数据
        return result;
      } else {
        return { value: undefined, done: true };
      }
    }
  }
}

上面代码为 arrayLike 对象添加 Symbol.iterator 方法,再使用for...of迭代,就可以按顺序取出value值了。

console.log("=========================");
for (const value of arrayLike) {
	console.log(value);
}
console.log("=========================");

在这里插入图片描述

三、为colorObj对象添加迭代器方法

回到开头我们所提的问题,在 colorObj 对象中,既没有 length 属性,对象的key也不是一组有序的数字,这时候我们如何像类数组一样为其添加 Iterator 接口呢?

const colorObj= {
	white: "小白",
	black: "小黑",
	gray: "小灰",
}

很简单,我们伪装它的length属性,并且按顺序排列它的key就可以了。

colorObj[Symbol.iterator] = function () {
  let keys = [];  // 对象中key的集合
  let length = 0;  // 伪装length属性
  for (const key in this) {
    if (Object.hasOwnProperty.call(this, key)) {  // 防止 for...in 遍历原型对象的属性
      keys.push(key);
      length++;
    }
  }
  let index = 0;
  return {
    next: () => {
      if (index < length) {
        const result = { value: this[keys[index]], done: false };
        index++;
        return result;
      } else {
        return { value: undefined, done: true };
      }
    }
  }
}
console.log("++++++++++++++++++++++++++++");
for (const value of colorObj) {
  console.log(value);
}
console.log("++++++++++++++++++++++++++++");

在这里插入图片描述

到这里就完成了对象的迭代了。

但现在还不算完,上面的代码还有优化空间,不知道你有没有听说过生成器 Generator 。

生成器 Generator ,可以将其理解成一个状态机,封装了多个内部状态。执行 Generator 函数会返回一个迭代器对象 Iterator,也就是说,Generator 函数除了状态机,还是一个迭代器对象生成函数。

形式上,Generator 函数是一个普通函数,但是有两个特征:

  1. function关键字与函数名之间有一个星号 *;
  2. 函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。
function* hello() {
  yield 'hello';
  yield 'world';
  yield 'ending';  // 使用return时,代表结束done:true;即使后面还存在值,也不返回。
}
var hw = hello();

console.log(hw.next());
console.log(hw.next());
console.log(hw.next());
console.log(hw.next());

在这里插入图片描述

四、优化colorObj代码

colorObj[Symbol.iterator] = function* () {
  let keys = [];
  let length = 0;
  for (const key in this) {
    if (Object.hasOwnProperty.call(this, key)) {
      keys.push(key);
      length++;
    }
  }
  let index = 0;
  while (index < length) {
    yield this[keys[index]];  // 生成器自动包装返回的值,且yield语句可以暂停while循环语句
    index++;
  }
}
let iterator = colorObj[Symbol.iterator]();  // 执行iterator方法,生成迭代器对象
console.log(iterator.next());  // 执行迭代器对象的next方法
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());

在这里插入图片描述

console.log("----------------------------");
for (const value of colorObj) {
  console.log(value);
}
console.log("----------------------------");

在这里插入图片描述

五、ES6内置迭代对象方法

最后,ES6中内置了迭代对象的方法:

Object.keys();     // 迭代对象的 key
Object.values();   // 迭代对象的 value
Object.entries();  // 迭代对象的 [key, value]

以 colorObj 为例:

const colorObj= {
	white: "小白",
	black: "小黑",
	gray: "小灰",
}
console.log("Object.keys", Object.keys(colorObj));
console.log("Object.values", Object.values(colorObj));
console.log("Object.entries", Object.entries(colorObj));

在这里插入图片描述

使用遍历命令for...of迭代对象:

for (const key of Object.keys(colorObj)) {
  console.log(key);
}
console.log("*******************************");
for (const value of Object.values(colorObj)) {
  console.log(value);
}
console.log("*******************************");
for (const [key, value] of Object.entries(colorObj)) {
  console.log(`${key}: ${value}`);
}

在这里插入图片描述


参考文章:
循环、递归、遍历、迭代四者的概念区别
可枚举属性、可迭代对象、迭代器(iterator)、for-in、for-of
JS的生成器详细使用、生成器结合迭代器使用
JavaScript之迭代器
es6:如何将一个普通对象转化为可迭代的对象

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/364417.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

hyper-v安装koolshare软路由

注意 windows更改适配器配置&#xff0c;为hostonly适配器配置两个IP hyper-v虚拟机内存设置为2G&#xff08;windows pe启动需要&#xff09; hyper-v虚拟机配置网络》选择hostonly网络 以管理员身份运行IMG写盘工具 BIOS从IDE启动 去掉DVD驱动器 添加硬件》网络适配器》wan网…

华为机试题:HJ84 统计大写字母个数(python)

文章目录&#xff08;1&#xff09;题目描述&#xff08;2&#xff09;Python3实现&#xff08;3&#xff09;知识点详解1、input()&#xff1a;获取控制台&#xff08;任意形式&#xff09;的输入。输出均为字符串类型。1.1、input() 与 list(input()) 的区别、及其相互转换方…

10 种主数据模型设计示例分享,推荐收藏

主数据模型是主数据管理的基础&#xff0c;一个完整的、可扩展的、相对稳定的主数据模型对于主数据管理的成功起着重要的作用。规划、创建主数据模型的过程&#xff0c;是梳理主数据管理体系的过程&#xff0c;目的是建立一个良好的资源目录结构&#xff0c;划分合理的资源粒度…

网关的通用设计框架

概念 网关&#xff0c;很多地方将网关比如成门&#xff0c; 没什么问题&#xff0c; 但是需要区分网关与网桥的区别。 网桥:工作在数据链路层&#xff0c;在不同或相同类型的LAN之间存储并转发数据帧&#xff0c;必要时进行链路层上的协议转换。可连接两个或多个网络&#xf…

从0到1一步一步玩转openEuler--22 管理服务-关闭、暂停、休眠系统服务

文章目录22 管理服务-关闭、暂停、休眠系统服务22.1 systemctl命令22.2 关闭系统22.3 重启系统22.4 使系统待机22.5 使系统休眠22 管理服务-关闭、暂停、休眠系统服务 22.1 systemctl命令 systemd通过systemctl命令可以对系统进行关机、重启、休眠等一系列操作。当前仍兼容部…

WSO2 apim 多租户来区分api

WSO2 apim 多租户来区分api1. Tenant1.1 Add new tenant1.2 Add Role/User1.3 Published Api2. Delete Teant3. AwakeningWSO2安装使用的全过程详解: https://blog.csdn.net/weixin_43916074/article/details/127987099. Official Document: Managing Tenants. 1. Tenant 1.1 …

电脑常用知识与工作常用工具

什么是电脑快捷键&#xff1f; 所谓快捷键就是使用键盘上某一个或某几个键的组合完成一条功能命令&#xff0c;从而达到提高操作速度的目的。 键盘布局 主键盘区&#xff0c;数字辅助键盘区、F键功能键盘区、控制键区&#xff0c;对于多功能键盘还增添了快捷键区 一、常用快捷…

多线程Thread常用方法和状态

Thread类 及常见方法 1、常见构造方法 方法说明Thread()创建线程对象Thread(Runnable target)使用 Runnable 对象创建线程对象Thread(String name)创建线程对象&#xff0c;并命名Thread(Runnable target, String name)使用 Runnable 对象创建线程对象&#xff0c;并命名Thre…

微信银行卡如何解除绑定?图文教程,快速解除

随着移动支付的普及&#xff0c;微信支付成为人们生活中不可或缺的支付方式。在微信支付中绑定银行卡可以让用户更方便地进行支付&#xff0c;但有时候需要解除银行卡的绑定。那么&#xff0c;微信银行卡如何解除绑定呢&#xff1f;在本文中&#xff0c;小编将详细介绍微信解除…

ZooKeeper实现分布式队列、分布式锁和选举详解

提示&#xff1a;本文章非原创&#xff0c;记录一下优秀的干货。 [原创参考]&#xff1a;https://blog.csdn.net/qq_40378034/article/details/117014648 前言 ZooKeeper源码的zookeeper-recipes目录下提供了分布式队列、分布式锁和选举的实现GitHub地址。 本文主要对这几种实…

XXL-JOB怎么玩?

官网&#xff1a;https://www.xuxueli.com/xxl-job/文档&#xff1a;https://www.xuxueli.com/xxl-job/#%E3%80%8A%E5%88%86%E5%B8%83%E5%BC%8F%E4%BB%BB%E5%8A%A1%E8%B0%83%E5%BA%A6%E5%B9%B3%E5%8F%B0XXL-JOB%E3%80%8BXXL-JOB主要有调度中心、执行器、任务。开放源产品&#…

数据的存储(2)——浮点型

前言&#xff1a;内容是关于浮点型在内存中的存储详解及例子 数据的存储&#xff08;1&#xff09;——整型&#xff08;点击即跳转&#xff09; 浮点数的存储规则 任意一个二进制浮点数可以表示成以下形式&#xff1a; (-1)^S * M * 2^E (-1)^S表示符号位&#xff0c;当S0…

企业数字化转型该怎么做?有效工具有哪些?

数字化转型的有效工具有哪些&#xff1f;简单来说&#xff0c;企业数字化转型的工具&#xff0c;可以划分为两大阶段—— 第一阶段是传统的IT软硬件&#xff0c;比如传统的ERP系统等第二阶段是与最新数字化技术相匹配的软硬件&#xff0c;比如“SaaS平台”、“低零代码平台”等…

数据结构与算法基础-学习-13-线性表之链队

一、个人理解链队是线性表的衍生之一&#xff0c;具有先进先出的特性&#xff0c;在队尾进行插入操作&#xff0c;在队头进行删除操作。链队由于是动态扩容的&#xff0c;需要新的数据节点时&#xff0c;分配一个&#xff0c;所以不存在顺序队的真上溢情况。链队删除队头节点&a…

点击化学交联剂1807518-78-0,Propargyl-PEG1-SS-PEG1-t-butyl ester,丙炔单乙二醇二硫键单乙二醇叔丁酯

1、基础产品数据&#xff08;Basic Product Data&#xff09;&#xff1a;CAS号&#xff1a;1807518-78-0中文名&#xff1a;丙炔-单乙二醇-二硫键-单乙二醇-叔丁酯英文名&#xff1a;Propargyl-PEG1-SS-PEG1-t-butyl ester 结构式&#xff08;Structural&#xff09;&#xff…

176、【动态规划】leetcode ——1143. 最长公共子序列(C++版本)

题目描述 原题链接&#xff1a;1143. 最长公共子序列 题目描述 本题和 718. 最长重复子数组&#xff08;动态规划&#xff09; 的区别在于此时不要求令一个数组中元素连续。 动态规划五步曲&#xff1a; &#xff08;1&#xff09;dp[i][j]含义&#xff1a; 截止到text1[i …

前端编译、JIT编译、AOT编译

一、前端编译&#xff1a;java设计之初就是强调跨平台&#xff0c;通过javac将源文件编译成于平台无关的class文件&#xff0c; 它定义了执行 Java 程序所需的所有信息&#xff08;许多Java"语法糖"&#xff0c;是在这个阶段完成的&#xff0c;不依赖虚拟机&#xff…

将多个springboot项目的pom.xml文件整合

将多个springboot项目的pom.xml文件整合 0.0、前因 ​ 刚入公司敲代码时、发现一个项目中会包含多个子项目、每个子项目会代表一个功能模块、这属实是把我这个菜鸟惊叹到了。而这种分而治之的方式也引申出一个问题&#xff1a;各子项目的依赖如何统一管理&#xff1f; ​ 我…

Linux:基于bufferevent epoll tcp客户端代码

基于bufferevent epoll tcp客户端代码: include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <string.h> #include <event2/event.h> #include <event2/buffere…

在外包干了几年,感觉自己都快费了

先说一下自己的情况。大专生&#xff0c;18年通过校招进入湖南某软件公司&#xff0c;干了接近2年的点点点&#xff0c;今年年上旬&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落&#xff01;而我已经在一个企业干了五年的功能测试…