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 ...
随机推荐
- Lua 5.3 协程简单示例
Lua 5.3 协程简单示例 来源 http://blog.csdn.net/vermilliontear/article/details/50547852 生产者->过滤器->消费者 模 ...
- HDU1255覆盖的面积
覆盖的面积 Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submis ...
- CentOS6.8通过yum安装MySQL5.7
Centos6.8通过yum安装mysql5.7 1.安装mysql的yum源 a.下载配置mysql的yum源的rpm包 根据上面3张图片中的操作下载下来的rpm文件可以通过如下命令获取: wget ...
- SVG绘制loading效果
<div class="loading"> <svg width='40px' height='40px' xmlns="http://www.w3.o ...
- 《Github入门与实践》读书笔记 蟲咋先生的追求之旅(上)
<Github入门与实践>作者: [日] 大塚弘记 译者:支鹏浩/刘斌 简介 本书从Git的基本知识和操作方法入手,详细介绍了GitHub的各种功能,GitHub与其他工具或服务的协作 ...
- freeMarker遍历map的正确方式
假设selectDateModel 是我们后台返回的map<String, String>; <#list selectDateModel?keys as key> <o ...
- 再学习之MyBatis
一.框架基本介绍 1.概念 支持普通SQL查询.存储过程和高级映射,简化和实现了Java 数据持久化层的的开源框架,主要流行的原因在于他的简单性和易使用性. 2.特点 持久层 .ORM(对象关系映射) ...
- selenium元素定位
在网页自动化测试中,我们要让程序自动模拟我们的点击.输入.悬浮.拖动等操作,完成我们的测试用例组. 输入.点击.打开这样的动词,已经包含在了selenium的方法中,可以直接调用(当然你也可以自己写) ...
- 阿里云全民云计算活动:云服务器ECS二折起(云主机)采购指南
首先要注册并登录阿里云,完成实名认证 可以用手机号新注册账号, 也可以使用淘宝账号直接登录,其他的登录方式还支持微博账号和支付宝账号等. 登录后如下图,先点"控制台", 然后鼠标移 ...
- Java字符编码
今天在往oracle数据库里插入数据时发现,往一个20字节的字段里插入8个汉字加上一个括号,并没有提示字段超长.猜想数据库应该并没有用万国码(utf-8). 查询数据库编码sql:select * f ...