热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JavaScript设计模式学习第二十二篇-迭代器模式

迭代器模式(IteratorPattern)用于顺序地访问聚合对象内部的元素,又无需知道对象内部结构。使用了迭代器之后,使用

迭代器模式(Iterator Pattern)用于顺序地访问聚合对象内部的元素,又无需知道对象内部结构。使用了迭代器之后,使用者不需要关心对象的内部构造,就可以按序访问其中的每个元素。


1. 什么是迭代器

银行里的点钞机就是一个迭代器,放入点钞机的钞票里有不同版次的人民币,每张钞票的冠字号也不一样,但当一沓钞票被放入点钞机中,使用者并不关心这些差别,只关心钞票的数量,以及是否有假币。

这里我们使用 Javascript 的方式来点一下钞:

var bills = ['MCK013840031', 'MCK013840032', 'MCK013840033', 'MCK013840034', 'MCK013840035'];bills.forEach(function(bill) {console.log('当前钞票的冠字号为 ' + bill)
})

是不是很简单,这是因为 Javascript 已经内置了迭代器的实现,在某些个很老的语言中,使用者可能会为了实现迭代器而烦恼,但是在 Javascript 中则完全不用担心。

 


2. 迭代器的简单实现

前面的 forEach 方法是在 IE9 之后才原生提供的,那么在 IE9 之前的时代里,如何实现一个迭代器呢,我们可以使用 for 循环自己实现一个 forEach:

var forEach = function(arr, cb) {for (var i = 0; i }forEach(['hello', 'world', '!'], function(currValue, idx, arr) {console.log('当前值 ' + currValue + ',索引为 ' + idx)
})// 当前值 hello,索引为 0
// 当前值 world,索引为 1
// 当前值 !,索引为 2

2.1. JQuery 源码中迭代器实现

JQuery 也提供了一个 $.each的遍历方法:

// jquery 源码
each: function (obj, callback) {var i = 0;// obj 为数组时if (isArrayLike(obj)) {for (; i }// 使用
$.each(['hello', 'world', '!'], function(idx, currValue){console.log('当前值 ' + currValue + ',索引为 ' + idx)
})

这里的源码分为两个部分,前一个部分是形参 obj 为数组情况下的处理,使用 for 循环,以数组下标依次使用 call/apply传入回调中执行,第二部分是形参 obj为对象情况下的处理,是使用 for-in 循环来获取对象上的属性。另外可以看到如果 callback.call返回的结果是 false 的话,这个循环会被 break。

源码位于: jquery/src/core.js#L246-L265

由于处理对象时使用的是 for-in,所以原型上的变量也会被遍历出来:

var foo = { paramProto: '原型上的变量' };var bar = Object.create(foo, {paramPrivate: {configurable: true,enumerable: true,value: '自有属性',writable: true}
});$.each(bar, function(key, currValue) {console.log('当前值为 「' + currValue + '」,键为 ' + key);
})// 当前值为 「自有属性」,键为 paramPrivate
// 当前值为 「原型上的属性」,键为 paramProto

因此可以使用 hasOwnProperty 来判断键是否是在原型链上还是对象的自有属性。

我们还可以利用如果 callback.call 返回的结果是 false 则 break 的特点,来进行一些操作:

$.each([1, 2, 3, 4, 5], function (idx, currValue) {if (currValue > 3){return false}console.log('当前值为 ' + currValue)
})
// 当前值为 1
// 当前值为 2
// 当前值为 3

2.2. Underscore 源码中的迭代器实现

// underscore 源码
_.each = function (obj, iteratee) {var i, length// obj 为数组时if (isArrayLike(obj)) {for (i = 0, length = obj.length; i }// 使用
_.each(['hello', 'world', '!'], function (currValue, idx, arr) {console.log('当前值 ' + currValue + ',索引为 ' + idx)
})

underscore 迭代器部分的实现跟 jQuery 的差不多,只是回调 iteratee 的执行是直接调用,而不是像 jQuery 是使用 call,也不像 jQuery 那样提供了迭代终止 break 的支持,所以总的来说还是 jQuery 的实现更优。

另外,这里 iteratee 变量的命名也可以看出来迭代器的含义。

源码位于: underscore.js#L181-L195

 


3. Javascript 原生支持

随着 Javascript 的 ECMAScript 标准每年的发展,给越来越多好用的 API 提供了支持,比如 Array 上的 filter、forEach、reduce、flat 等,还有 Map、Set、String 等数据结构,也提供了原生的迭代器支持,给我们的开发提供了很多便利,也让 underscore 这些工具库渐渐淡出历史舞台。

另外,Javascript 中还有很多类数组结构,比如:

1. arguments:函数接受的所有参数构成的类数组对象;

2. NodeList:是 querySelector接口族返回的数据结构;

3. HTMLCollection:是 getElementsBy 接口族返回的数据结构;

对于这些类数组结构,我们可以通过一些方式来转换成普通数组结构,以 arguments为例:

// 方法一
var args = Array.prototype.slice.call(arguments)// 方法二
var args = [].slice.call(arguments)// 方法三 ES6提供
const args = Array.from(arguments)// 方法四 ES6提供
const args = [...arguments];

转换成数组之后,就可以快乐使用 Javascript 在 Array 上提供的各种方法了。

 


4. ES6 中的迭代器

ES6 规定,默认的迭代器部署在对应数据结构的 Symbol.iterator 属性上,如果一个数据结构具有 Symbol.iterator 属性,就被视为可遍历的,就可以用 for...of 循环遍历它的成员。也就是说,for...of 循环内部调用的是数据结构的Symbol.iterator 方法。

for-of 循环可以使用的范围包括 Array、Set、Map 结构、上文提到的类数组结构、Generator 对象,以及字符串。

通过 for-of 可以使用 Symbol.iterator 这个属性提供的迭代器可以遍历对应数据结构,如果对没有提供 Symbol.iterator 的目标使用 for-of 则会抛错:

var foo = { a: 1 }for (var key of foo) {console.log(key)
}// Uncaught TypeError: foo is not iterable

我们可以给一个对象设置一个迭代器,让一个对象也可以使用 for-of 循环:

var bar = {a: 1,[Symbol.iterator]: function() {var valArr = [{ value: 'hello', done: false },{ value: 'world', done: false },{ value: '!', done: false },{ value: undefined, done: true }]return {next: function() {return valArr.shift()}}}
}for (var key of bar) {console.log(key)
}// hello
// world
// !

可以看到 for-of 循环连 bar 对象自己的属性都不遍历了,遍历获取的值只和 Symbol.iterator 方法实现有关。

 


5. 迭代器模式总结

迭代器模式早已融入我们的日常开发中,在使用 filter、reduce、map 等方法的时候,不要忘记这些便捷的方法就是迭代器模式的应用。当我们使用迭代器方法处理一个对象时,我们可以关注与处理的逻辑,而不必关心对象的内部结构,侧面将对象内部结构和使用者之间解耦,也使得代码中的循环结构变得紧凑而优美。

 


推荐阅读
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • javascript  – 概述在Firefox上无法正常工作
    我试图提出一些自定义大纲,以达到一些Web可访问性建议.但我不能用Firefox制作.这就是它在Chrome上的外观:而那个图标实际上是一个锚点.在Firefox上,它只概述了整个 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • Android JSON基础,音视频开发进阶指南目录
    Array里面的对象数据是有序的,json字符串最外层是方括号的,方括号:[]解析jsonArray代码try{json字符串最外层是 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
author-avatar
材女貝蒂_673_576
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有