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 ...
随机推荐
- Lua5.3 注册表 _G _ENV
Lua5.3 注册表 _G _ENV 来源:http://blog.csdn.net/murisly/article/details/46518551 注册表的描述,借用PIL中的一段话: regis ...
- poj 3662 Telephone Lines
Telephone Lines Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 7115 Accepted: 2603 D ...
- 【JDK1.8】JDK1.8集合源码阅读——LinkedHashMap
一.前言 在上一篇随笔中,我们分析了HashMap的源码,里面涉及到了3个钩子函数,用来预设给子类--LinkedHashMap的调用,所以趁热打铁,今天我们来一起看一下它的源码吧. 二.Linked ...
- AngularJS学习篇(十六)
AngularJS 表单 HTML 控件 以下 HTML input 元素被称为 HTML 控件: input 元素 select 元素 button 元素 textarea 元素 HTML 表单 H ...
- iPad web APP 开发相关
1.移除 browser chrome,全屏启动 <meta name="apple-mobile-web-app-capable" content="yes&q ...
- python的xlwt模块的常用方法
工作中需要导出数据为excel格式,使用了xlwt模块,在此记录一下用到的内容. 1. 创建一个表,设置一个sheet import xlwt workbook = xlwt.Workbook(enc ...
- SQLServer2008R2 发布订阅及相关问题解决办法
前言: 前两天接到领导的任务,将一个系统A的客户数据同步到另一个系统B中,以后客户录入入口只有A系统,B系统不提供录入入口,因为各种原因不能使用接口方式A系统和B系统直接交互同步,只能通过数据库重A库 ...
- .Net中各种不同的对象创建方式的速度差异
在.Net中,微软给我们提供了很多不同的创建对象实例的方法,它们的速度又各有不同,以下一一列举. 使用new关键字 这在.Net中是最常见,也是速度最快的方式: ...
- RabbitMQ之比较好的资料
http://mysql.taobao.org/index.php/Rabbitmq http://www.cnblogs.com/me-sa/archive/2012/10/17/rabbitmq_ ...
- 使用swiper简单的h5下滑翻页效果,
<!DOCTYPE html><html lang="en"><head> <meta charset="utf-8" ...