lodash源码分析之compact中的遍历
小时候,
乡愁是一枚小小的邮票,
我在这头,
母亲在那头。
长大后,乡愁是一张窄窄的船票,
我在这头,
新娘在那头。
后来啊,
乡愁是一方矮矮的坟墓,
我在外头,
母亲在里头。
而现在,
乡愁是一湾浅浅的海峡,
我在这头,
大陆在那头。
——余光中《乡愁》
本文为读 lodash 源码的第三篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodash
gitbook也会同步仓库的更新,gitbook地址:pocket-lodash
作用与用法
compact 函数用来去除数组中的假值,并返回由不为假值元素组成的新数组。
false、null、0、 ""、undefined 和 NaN 都为假值。
例如:
var arr = [1,false,2,null,3,0,4,NaN,5,undefined]
_.compact(arr) // 返回 [1,2,3,4,5]
源码
function compact(array) {
let resIndex = 0
const result = []
if (array == null) {
return result
}
for (const value of array) {
if (value) {
result[resIndex++] = value
}
}
return result
}
compact 的源码只有寥寥几行,相当简单。
首先判断传入的数组是否为 null 或者 undefined,如果是,则返回空数组。
然后用 for...of 来取得数组中每项的值,如果不为假值,则存入新数组 result 中,最后将新数组返回。
到这里,源码分析完了。
但是在看源码的时候,发现这里用了 for...of 来做遍历,其实除了 for...of 外,也可以用 for 或者 for...in 来做遍历,那为什么最后选了 for...of 呢?
数组中的for循环
使用 for 循环,很容易就将 compact 中关于循环部分的源码改写成以下形式:
for (let i = 0; i < array.length; i++) {
const value = array[i]
if (value) {
result[resIndex++] = value
}
}
这样写,肯定是没有问题的,但是不够简洁。
for…in
再来看 for...in 循环,先来将源码改写一下:
for (let index in array) {
const value = array[i]
if (value) {
result[resIndex++] = value
}
}
先看看MDN上关于 for...in 的用法:
for...in语句以任意顺序遍历一个对象的可枚举属性。
关于可枚举属性,可以点击上面的链接到MDN上了解一下,这里不做太多的解释。
在数组中,数组的索引是可枚举属性,可以用 for...in 来遍历数组的索引,数组中的稀疏部分不存在索引,可以避免用 for 循环造成无效遍历的弊端。
但是,for...in 有两个致命的特性:
for...in的遍历不能保证顺序for...in会遍历所有可枚举属性,包括继承的属性。
for...in 的遍历顺序依赖于执行环境,不同执行环境的实现方式可能会不一样。单凭这一点,就断然不能在数组遍历中使用 for...in,大多数情况下,顺序对于数组的遍历都相当重要。
关于第二点,先看个例子:
var arr = [1,2,3]
arr.foo = 'foo'
for (let index in arr) {
console.log(index)
}
在这个例子中,你期望输出的是 0,1,2,但是最后输出的可能是 0,1,2,foo (for...in 不能保证顺序)。因为 foo 也是可枚举属性,在 for..in 会被遍历出来。
for…of
最后来看看 for...of。
当我们在控制台中打印一个数组,并将它展开来查看时,会在数组的原型链上发现一个很特别的属性 Symbol.iterator。
其实 for...of 循环内部调用的就是数组原型链上的 Symbol.iterator 方法。
Symbol.iterator 在调用的时候会返回一个遍历器对象,这个遍历器对象中包含 next 方法,for...of 在每次循环的时候都会调用 next 方法来获取值,直到 next 返回的对象中的 done属性值为 true 时停止。
其实我们也可以手动调用来模拟遍历的过程:
const arr = [1,2,3]
const iterator = a[Symbol.iterator]()
iterator.next() // {value: 1, done: false}
iterator.next() // {value: 2, done: false}
iterator.next() // {value: 3, done: false}
iterator.next() // {value: undefined, done: true}
知道这些原理后,完全可以改写数组中的 Symbol.iterator 方法,例如遍历时将数组中的值都乘2:
Array.prototype[Symbol.iterator] = function () {
let index = 0
const _self = this
return {
next: function () {
if (index < _self.length) {
return {value: _self[index++] * 2, done: false}
} else {
return {done: true}
}
}
}
}
使用 Generator 函数可以写成以下的形式:
Array.prototype[Symbol.iterator] = function* () {
let index = 0
while (index < this.length) {
yield this[index++] * 2
}
}
因此在不改写 Symbol.iterator 的情况下,使用 for...of 来遍历数组是安全的,因为这个方法是数组的原生方法。
关于 Iterator 和 Generator 可以点击参考中的链接详细查看。
参考
License
署名-非商业性使用-禁止演绎 4.0 国际 (CC BY-NC-ND 4.0)
最后,所有文章都会同步发送到微信公众号上,欢迎关注,欢迎提意见: 
作者:对角另一面
lodash源码分析之compact中的遍历的更多相关文章
- lodash源码分析之baseFindIndex中的运算符优先级
我悟出权力本来就是不讲理的--蟑螂就是海米:也悟出要造反,内心必须强大到足以承受任何后果才行. --北岛<城门开> 本文为读 lodash 源码的第十篇,后续文章会更新到这个仓库中,欢迎 ...
- lodash源码分析之自减的两种形式
这个世界需要一个特定的恶人,可以供人们指名道姓,千夫所指:"全都怪你". --村上春树<当我谈跑步时我谈些什么> 本文为读 lodash 源码的第六篇,后续文章会更新到 ...
- lodash源码分析之List缓存
昨日我沿着河岸/漫步到/芦苇弯腰喝水的地方 顺便请烟囱/在天空为我写一封长长的信 潦是潦草了些/而我的心意/则明亮亦如你窗前的烛光/稍有暧昧之处/势所难免/因为风的缘故 --洛夫<因为风的缘故& ...
- lodash源码分析之缓存方式的选择
每个人心里都有一团火,路过的人只看到烟. --<至爱梵高·星空之谜> 本文为读 lodash 源码的第八篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodash gitb ...
- lodash源码分析之缓存使用方式的进一步封装
在世界上所有的民族之中,支配着他们的喜怒选择的并不是天性,而是他们的观点. --卢梭<社会与契约论> 本文为读 lodash 源码的第九篇,后续文章会更新到这个仓库中,欢迎 star:po ...
- lodash源码分析之数组的差集
外部世界那些破旧与贫困的样子,可以使我内心世界得到平衡. --卡尔维诺<烟云> 本文为读 lodash 源码的第十七篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodas ...
- angular源码分析:angular中脏活累活的承担者之$interpolate
一.首先抛出两个问题 问题一:在angular中我们绑定数据最基本的方式是用两个大括号将$scope的变量包裹起来,那么如果想将大括号换成其他什么符号,比如换成[{与}],可不可以呢,如果可以在哪里配 ...
- angular源码分析:angular中入境检察官$sce
一.ng-bing-html指令问题 需求:我需要将一个变量$scope.x = '<a href="http://www.cnblogs.com/web2-developer/&qu ...
- angular源码分析:angular中各种常用函数,比较省代码的各种小技巧
angular的工具函数 在angular的API文档中,在最前面就是讲的就是angular的工具函数,下面列出来 angular.bind //用户将函数和对象绑定在一起,返回一个新的函数 angu ...
随机推荐
- 面试题----寻找比一个N位数大的“下”一个数
题目描述 写出一个算法,实现如下功能: 给定一个N位数字组成的数,找出比这个数大的由相同数字组成的下一个数 例如:如果数字为 25468, 则结果为25486 如果数字为 21765, 则结果为 25 ...
- 【JDK1.8】JDK1.8集合源码阅读——LinkedHashMap
一.前言 在上一篇随笔中,我们分析了HashMap的源码,里面涉及到了3个钩子函数,用来预设给子类--LinkedHashMap的调用,所以趁热打铁,今天我们来一起看一下它的源码吧. 二.Linked ...
- Linux系列教程(十四)——Linux用户和用户组管理之相关配置文件
前面我们介绍了软件包管理.首先介绍了rpm包的相关命令,但是我们发现直接安装rpm包会被其依赖性折磨的不行,然后解决办法是yum在线管理,通过yum命令安装rpm包能自动帮助我们解决依赖性.最后又介绍 ...
- C#设计模式之十三代理模式(Proxy)【结构型】
一.引言 今天我们要讲[结构型]设计模式的第七个模式,也是"结构型"设计模式中的最后一个模式,该模式是[代理模式],英文名称是:Proxy Pattern.还是老套路,先从名字 ...
- 小米/红米导入VCF联系人乱码问题解决
PS:尽量不要用什么豌豆荚啊.微信啊.QQ啊之类的通讯录备份,那就等于把自己的通讯录免费送给腾讯他们了....还是自己手动的好一些,但是小白用户或者经常丢手机的卖就卖吧,总比联系人都丢了要好~~~ 默 ...
- 【APP问题定位(一)】夜神模拟器安装及问题
本文较少下夜神模拟器的使用,包括了夜神安装,APK安装,开发者选项打开. 安装夜神模拟器 到 夜神模拟器 网站自行下载安装文件,双击exe文件安装,选择"自定义安装" ...
- Maven中settings.xml的配置项说明精讲
1.Maven的配置文件(Maven的安装目录/conf/settings.xml ) 和 Maven仓库下(默认的Maven仓库的是用户家目录下的.m2文件,可以另行制定)的settings.xml ...
- asp .net连接打开数据库初步
1 #endregion59 WebDriver
- C#操作防火墙控制电脑某些软件联网
问题: 目前公司软件刚由单机软件更改为联网软件,许多客户反映希望能够有一个功能来控制电脑上某些必用软件,如qq,公司软件联网,而其他不必要的如网页,游戏等软件不允许联网,于是向公司反映希望可以有一个功 ...
- ORA-01745: 无效的主机/绑定变量名 ORA-00917: 缺失的逗号 oracle日期格式错误
今天在oracle中执行插入语句的时候报了一个奇怪的错误,在程序中报的错误是ORA-01745: 无效的主机/绑定变量名,网上一查说是缺失逗号,在查询分析器执行的时候报缺失的逗号,仔细看了一下也没有缺 ...