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 ...
随机推荐
- Ruby on Rails---Active Admin使用(一)
概述 Active Admin提供了一个友好的后台管理界面,将CRUD等操作可视化,操作极其方便 安装 1. 添加gem gem "devise", :github => ' ...
- 2-SAT问题总结
2-SAT问题总结 2-SAT问题:n个布尔型的变量,给出m个约束条件,约束条件例如:A,B不能同时为真,A,B必须同时为真等. 看了算法入门经典中的解决办法,关于这种解决办法比较容易理解,并且效率也 ...
- HDU1792A New Change Problem(GCD规律推导)
A New Change Problem Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Oth ...
- Constructing Roads(最小生成树)
Constructing Roads Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) ...
- linux进程资源占用高原因分析命令记录
1.查看进程的线程: ps -eLf|egrep 'gateserver|UID' 2.跟踪线程调用: strace -p 15530 3.统计线程中函数的调用小号CPU时间: strace -p 1 ...
- jsoup从表单中取数据
表单的格式如下 <td>user</td> <td>cc</td> </tr> <tr> <td>pass</ ...
- 验证Oracle处理速度
(这是2009年写的东西了,在网上看到有人对数据库批量操作的'速度'比较关注,于是就把这篇老文章整理了一下) 一.环境及前提 在244上(一台稍好一些的机器,做了RAID,机械硬盘,Raid几忘了), ...
- Web桌面应用框架3:Web桌面应用开发的N种Style
研究Web桌面应用开发有一段时间了,总结了Web桌面应用开发的一些主流方式. 一.前端Style 这种方式的就是直接实现一个Web程序,再封装一个浏览器展示,相当粗暴和有效.著名的框架就是Electr ...
- spring返回@ResponseBody报406
HTTP Status 406 - type Status report message description The resource identified by this request is ...
- OpenCV二维Mat数组(二级指针)在CUDA中的使用
CUDA用于并行计算非常方便,但是GPU与CPU之间的交互,比如传递参数等相对麻烦一些.在写CUDA核函数的时候形参往往会有很多个,动辄达到10-20个,如果能够在CPU中提前把数据组织好,比如使用二 ...