javascript 设计模式之迭代器模式
2021/5/18 22:28:44
本文主要是介绍javascript 设计模式之迭代器模式,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
概念
迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。
特点:
- 为遍历不同数据结构的 “集合” 提供统一的接口;
- 能遍历访问 “集合” 数据中的项,不关心项的数据结构
从 Array.prototype.forEach 说起
let arr = [1, 2, 3] arr.forEach((item) => { console.info(item) // 1 2 3 })
在数组上遍历是没有问题的,但将它作为在 NodeList 就会发现报错
<div id="div1"> <a href="#">a1</a> <a href="#">a2</a> <a href="#">a3</a> <a href="#">a4</a> <a href="#">a5</a> </div>
遍历 id 为 div1 下的所有 a 标签
let nodeList = document.getElementById('div1').getElementsByTagName('a') nodeList.forEach((item) => { console.info(item) })
运行会报Uncaught TypeError: nodeList.forEach is not a function
错误。
这是由于 nodeList 只是类数组,并没有实现 forEach 方法。
使用 for 循环遍历
let nodeList = document.getElementById('div1').getElementsByTagName('a') let i, length = nodeList.length for (i = 0; i < length; i++) { console.info(nodeList[i].innerHTML) }
可以正常打印
jQuery 的 each 方法
上述的数组跟 nodeList 采用不同的遍历方式,为了处理这种情况,通过借助jQuery的each方法,我们可以用同一套遍历规则遍历不同的集合对象:
let arr = [1, 2, 3] let nodeList = document.getElementById('div1').getElementsByTagName('a') let $a = $('a') $.each(arr, function (index, item) { console.log(`数组的第${index}个元素是${item}`) }) $.each(nodeList, function (index, aNode) { console.log(`DOM类数组的第${index}个元素是${aNode.innerText}`) }) $.each($a, function (index, aNode) { console.log(`jQuery集合的第${index}个元素是${aNode.innerText}`) })
可以正常输出内容
自定义个迭代器
// 主体 class Container { constructor(list) { this.list = list } // 生成遍历器 getIterator() { return new Iterator(this) } } // 迭代器 class Iterator { constructor(container) { this.list = container.list this.index = 0 } next() { if (this.hasNext()) { return this.list[this.index++] } return null } hasNext() { if (this.index < this.list.length) { return true } return false } } const arr = [1, 2, 3, 4, 5, 6] // 是有序的集合,比如数组,nodeList const container = new Container(arr) const iterator = container.getIterator() while (iterator.hasNext()) { console.info(iterator.next()) }
ES6 之后的迭代器
ES6 新增了 Set 和 Map ,导致目前有序集合有 Array Map Set String TypedArray argument NodeList 这么多种,需要一个统一的遍历接口访问这些有序集合中的数据,所以 ES6 引入了 Iterator。
ES6 默认的 Iterator 接口部署在数据结构的 [Symbol.iterator] 属性上,该属性本身是一个函数,执行这个函数会返回个遍历器对象。
遍历器对象的特征:
- 拥有 next 方法
- 执行 next(),会返回个包含 value 和 done 属性的对象
- value: 当前数据结构成员的值
- done: 布尔值,表示遍历是否结束
在数组上测试下
let arr = [1, 2, 3] let iterator = arr[Symbol.iterator](); console.info(iterator.next()) // { value: '1', done: false } console.info(iterator.next()) // { value: '2', done: false } console.info(iterator.next()) // { value: '3', done: false } console.info(iterator.next()) // { value: undefined, done: false }
代码优化并封装个 each 方法:
function each(data) { const iterator = data[Symbol.iterator]() // 生成迭代器 let item = { done: false } while (!item.done) { item = iterator.next() if (!item.done) { console.info(item) console.info(item.value) } } } let arr = [1, 2, 3] let nodeList = document.getElementsByTagName('P') let map = new Map() map.set('name', 'zhangSan') map.set('age', 12) each(arr) each(nodeList) each(map)
只要具有 [Symbol.iterator] 属性的集合,就可以使用 each 方法进行遍历
for of
Symbol.iterator并不是人人都知道的,也不是每个人都需要封装一个 each 方法,因此,ES6提供了一个新的方法 for…of 来遍历:
// for...of 自动遍历拥有 Iterator 接口的数据结构 let arr = [1, 2, 3]; for (let item of arr) { console.log(item); } // 输出:1 2 3
说明 for…of 只是个语法糖,运行原理:
- 首先调用遍历对象 Symobo.iterator 方法,拿到遍历器对象;
- 每次循环,调用遍历器对象 next() 方法,得到 {value: …, done: … } 对象
等价于下面:
// 通过调用iterator,拿到迭代器对象 const iterator = arr[Symbol.iterator]() // 初始化一个迭代结果 let now = { done: false } // 循环往外迭代成员 while(!now.done) { now = iterator.next() if(!now.done) { console.log(`现在遍历到了${now.value}`) } }
ES6 Iterator 和 Generator
Iterator 的价值不限于上述几个类型的遍历,还有 Generator 函数的使用。Generator 返回的数据符合 Iterator 接口遍历的要求,所以 Generator 函数也可以使用 Iterator 语法。
function* helloWorldGenerator() { yield 'hello' yield 'world' return 'ending' } // var hw = helloWorldGenerator() // console.info(hw) // hw.next() //{value: "hello", done: false} // hw.next() //{value: "world", done: false} // hw.next() //{value: "ending", done: true} // hw.next() //{value: undefined, done: true} // console.info(hw[Symbol.iterator]) for (var item of helloWorldGenerator()) { console.info(item) // hello world }
根据这个特点可以用 for…of 遍历普通对象
for…of 遍历普通对象的解决方法:
- 使用 Objet.keys 将对象键名生成一个数组,然后遍历该数组;
- Generator 函数重新包装对象
let person = { name: 'Ken', sex: 'Male' } // Generator 包装对象 function* entries(obj) { for (let key of Object.keys(obj)) { yield [key, obj[key]]; } } for (let [key, value] of entries(person)) { console.log(`${key}: ${value}`); } // 输出: // name: Ken // sex: Male
迭代器分类
内部迭代器
内部已经定义好了迭代规则,它完全接手整个迭代过程,外部只需要一次初始调用
实现:
function each(arr, fn) { for (let i = 0; i < arr.length; i++) { fn(i, arr[i]) } } each([1, 2, 3], function(i, n) { console.log(i) // 0 1 2 console.log(n) // 1 2 3 })
优缺点:
- 优点:内部迭代器在调用的时候非常方便,外界不用关心迭代器内部的实现,跟迭代器的交互也仅仅是一次初始调用
- 缺点:由于内部迭代器的迭代规则已经被提前规 定,上面的 each 函数就无法同时迭代2个数组,比如要比较两数组是否相等, 只能在其回调函数中作文章了, 代码如下:
var compare = function( ary1, ary2 ){ if ( ary1.length !== ary2.length ){ throw new Error ( 'ary1 和ary2 不相等' ); } each( ary1, function( i, n ){ if ( n !== ary2[ i ] ){ throw new Error ( 'ary1 和ary2 不相等' ); } }); alert ( 'ary1 和ary2 相等' ); }; compare( [ 1, 2, 3 ], [ 1, 2, 4 ] ); // throw new Error ( 'ary1 和ary2 不相等' );
jQuery 的 $.each 以及 for…of 也是内部迭代器
外部迭代器
外部迭代器必须显式地请求迭代下一个元素
上面的"自定义个迭代器" 以及 Generator yield 就是外部迭代器。
解决下两个数组比较问题:
const arr1 = [1, 2, 3], arr2 = [1, 2, 3] const container1 = new Container(arr1), container2 = new Container(arr2) const iterator1 = container1.getIterator(), iterator2 = container2.getIterator() function compare(iterator1, iterator2) { while (iterator1.hasNext() || iterator2.hasNext()) { if (iterator1.next() !== iterator2.next()) { return false } } return true } console.info(compare(iterator1, iterator2))
优缺点:
- 优点:外部迭代器将遍历的权利转移到外部, 因此在调用的时候拥有了更多的自由性,
- 缺点:调用方式较复杂
这篇关于javascript 设计模式之迭代器模式的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-10-06小米11i印度快充版ROM合集:极致体验,超越期待
- 2024-10-06【ROM下载】小米11i 5G 印度版系统, 疾速跃迁,定义新速度
- 2024-10-06【ROM下载】小米 11 青春活力版,青春无极限,活力全开
- 2024-10-05小米13T Pro系统合集:性能与摄影的极致融合,值得你升级的系统ROM
- 2024-10-01基于Python+Vue开发的医院门诊预约挂号系统
- 2024-10-01基于Python+Vue开发的旅游景区管理系统
- 2024-10-01RestfulAPI入门指南:打造简单易懂的API接口
- 2024-10-01初学者指南:了解和使用Server Action
- 2024-10-01Server Component入门指南:搭建与配置详解
- 2024-10-01React 中使用 useRequest 实现数据请求