当前位置: 首页 > news >正文

前端开发之迭代器模式

在前端开发中,设计模式是提升代码可读性、可扩展性和可维护性的关键。迭代器模式(Iterator Pattern)是行为型设计模式中的一种,能够让我们顺序访问一个集合中的元素,而不暴露其底层的结构。在 TypeScript 这样具有类型检查的语言中,迭代器模式应用更加方便。

简单for循环

迭代器模式解决的问题是如何更加方便,更加简易,更加友好地去遍历一个顺序集合。用for循环不就行了?不行,普通for循环不是迭代器,它有两个缺点,不符合我们的设计原则。

const arr = [10, 20, 30]
const length = arr.length
for (let i = 0; i < length; i++) {console.log(arr[i])
}

如上代码,首先需要知道数组的长度 length ,需要知道如何获取数组的元素 arr[i] 。这样就不符合高内聚低耦合的设计原则,这样数组的很多信息就被公开了。作为一个数组被公开没什么问题,但是如果这个数组是一个比较复杂的数据结构呢?或者第三方服务呢?把场景放大就会是很大的问题。所以普通for循环不是迭代器,迭代器解决的问题是解决数据源里面数据不确定问题、数据封装问题,更好地遍历有序集合的问题。

简易迭代器

const pList = document.querySelectorAll("p");
pList.forEach((p) => console.log(p));

这里没有使用for循环,而使用forEach,本质的区别是,forEach遍历的时候,不需要知道数组长度,也不需要知道怎么获取一个 元素,它直接通过参数传过来了。 

迭代器模式

UML类图如下:


class DataIterator {private data: number[];private index: number = 0;constructor(container: DataContainer) {this.data = container.data;}next() {if (this.hasNext()) {return this.data[this.index++];}return null;}hasNext() {return this.index < this.data.length;}
}class DataContainer {data = [10, 11, 12, 13, 14, 15];getIterator() {return new DataContainer(this);}
}const dataContainer = new DataContainer();
const dataIterator = new DataIterator(dataContainer);
while (dataIterator.hasNext()) {console.log(dataIterator.next());
}

以上输出结果和for循环遍历是一样的,但我们费这么大力气是为了什么呢?就是为了掩饰迭代器背后它是怎样不用for循环,不用获取长度、不用通过index获取数据的这种高内聚低耦合的方式,更符合设计原则:使用者和目标分离解耦;目标能自行控制其内部逻辑;使用者不关心目标的内部结构。 

 迭代器场景

1. 有序结构。包含:字符串、数组、NodeList等DOM集合、Map、Set、arguments

2. Symbol.iterator 

所有有序对象,都内置Symbol.iterator方法,执行该方法,会返回一个迭代器对象。

done为false,说明还没有到最后一个。和我们的 hasNext() 差不多。

我们可以自己定义一个迭代器。

interface IteratorRes {value: number | undefined;done: boolean;
}class CustomIterator {private length = 3;private index = 0;next(): IteratorRes {this.index++;if (this.index <= this.length) {return { value: this.index, done: false };}return { value: undefined, done: true };}[Symbol.iterator]() {return this;}
}const customIterator = new CustomIterator();
console.log(customIterator.next());
console.log(customIterator.next());
console.log(customIterator.next());
console.log(customIterator.next());

迭代器作用

1. 用于for of

只要有内置了Symbol.iterator,都可以执行。

const set = new Set([1, 2, 3]);
for (const num of set) {console.log(num);
}
const pList = document.querySelectorAll("p");
for (const p of pList) {console.log(p);
}
// 包括我们上面的例子
const customIterator = new CustomIterator();
for (const ci of customIterator) {console.log(ci);

2. 数组解构、扩展操作符、Array.from (可用于有任何迭代器模式的数据结构)

// 解构 数组
const arr = [10, 11, 12];
const [a1, a2] = arr;
console.log("a1", a1); // 10
console.log("a2", a2); // 11
// 解构 Set
const set = new Set([100, 200, 300]);
const [s1, s2] = set;
console.log("s1", s1); // 100
console.log("s2", s2); // 200// 扩展操作符
const arr = [10, 11, 12];
console.log([...arr]) // [10, 11, 12]
const set = new Set([100, 200, 300]);
console.log([...set]) // [100, 200, 300]// Array.form
console.log(Array.from(set)) // [100, 200, 300]

3. 用于创建Map和Set

4. 用于 Promise.all 和 Promise.race

5. 用于 yield*

Generator 生成器

function* getNums(){yield 10yield 20yield 30
}
const iterator = getNums()
console.log(iterator.next()) // {value: 10, done: false}
console.log(iterator.next()) // {value: 20, done: false}
console.log(iterator.next()) // {value: 30, done: false}
console.log(iterator.next()) // {value: undefined, done: true}
// 将上面的 console.log注释,接下来使用 for of
for (const number of iterator) {console.log(number) // 10 20 30
}

可见生成器返回的就是一个迭代器。

function* getNums(){yield* [10,20,30] // 有序结构,已经实现了[Symbol.Iterator]
}
const iterator = getNums()
for (const number of iterator) {console.log(number) // 10,20,30
}

接下来重构一下上面的 CustomIterator

class CustomIterator {private data: number[];constructor() {this.data = [10, 20, 30];}*[Symbol.iterator]() {yield* this.data;}
}
const iterator = new CustomIterator();
for (const n of iterator) {console.log(n); // 10 20 30
}

接下来我们使用 Generator + yield 深度优先遍历 DOM 树

<div id="app"><p><b>hello</b><i>world</i></p><p><img src="https://www.baidu.com/img/PCfb_5bf082d29588c07f842ccde3f97243ea.png" /></p>
</div>
function* traverse(elementList: Element[]): any {for (const element of elementList) {yield element;const children = Array.from(element.children);if (children.length) {yield* traverse(children);}}
}
const iterator = document.getElementById("app");
if (iterator) {for (const iteratorElement of traverse([iterator])) {console.log(iteratorElement);}
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 从数据仓库到数据中台再到数据飞轮:我了解的数据技术进化史
  • 代码管理-使用TortoiseGit同步项目到Github/Gitee
  • 运行npm install 时,卡在sill idealTree buildDeps没有反应
  • SCRM电商管理后台Axure高保真原型 源文件
  • 电脑提示丢失mfc140u.dll的详细解决方案,mfc140u.dll文件是什么
  • C++初阶:STL详解(五)——vector的模拟实现
  • 初中生物--7.生物圈中的绿色植物(二)
  • java项目之在线考试与学习交流网页平台源码(springboot)
  • QT 串口上位机读卡显示
  • 枚举(not二分)
  • TCP 和 UDP 协议的区别?
  • MySQL之约束
  • Python列表循环的两种方法
  • 图书管理系统(面向对象的编程练习)
  • 渗透测试综合靶场 DC-1 通关详解
  • 【编码】-360实习笔试编程题(二)-2016.03.29
  • canvas 高仿 Apple Watch 表盘
  • C学习-枚举(九)
  • java8-模拟hadoop
  • Javascript编码规范
  • JavaScript对象详解
  • Java小白进阶笔记(3)-初级面向对象
  • Perseus-BERT——业内性能极致优化的BERT训练方案
  • Spark学习笔记之相关记录
  • uva 10370 Above Average
  • v-if和v-for连用出现的问题
  • vue从创建到完整的饿了么(18)购物车详细信息的展示与删除
  • Yii源码解读-服务定位器(Service Locator)
  • 阿里云爬虫风险管理产品商业化,为云端流量保驾护航
  • 表单中readonly的input等标签,禁止光标进入(focus)的几种方式
  • 基于组件的设计工作流与界面抽象
  • 前端性能优化--懒加载和预加载
  • 算法-图和图算法
  • 我的面试准备过程--容器(更新中)
  • 赢得Docker挑战最佳实践
  • 由插件封装引出的一丢丢思考
  • 自动记录MySQL慢查询快照脚本
  • 从如何停掉 Promise 链说起
  • ​浅谈 Linux 中的 core dump 分析方法
  • ​软考-高级-信息系统项目管理师教程 第四版【第23章-组织通用管理-思维导图】​
  • (4)(4.6) Triducer
  • (9)目标检测_SSD的原理
  • (Demo分享)利用原生JavaScript-随机数-实现做一个烟花案例
  • (第三期)书生大模型实战营——InternVL(冷笑话大师)部署微调实践
  • (十五)使用Nexus创建Maven私服
  • (图文详解)小程序AppID申请以及在Hbuilderx中运行
  • (未解决)jmeter报错之“请在微信客户端打开链接”
  • (五)activiti-modeler 编辑器初步优化
  • (五)IO流之ByteArrayInput/OutputStream
  • (五)大数据实战——使用模板虚拟机实现hadoop集群虚拟机克隆及网络相关配置
  • (译)计算距离、方位和更多经纬度之间的点
  • (转)利用PHP的debug_backtrace函数,实现PHP文件权限管理、动态加载 【反射】...
  • ***php进行支付宝开发中return_url和notify_url的区别分析
  • ./configure、make、make install 命令
  • .bat批处理(六):替换字符串中匹配的子串