lodash源码分析之compact中的遍历

小时候,

乡愁是一枚小小的邮票,

我在这头,

母亲在那头。

长大后,乡愁是一张窄窄的船票,

我在这头,

新娘在那头。

后来啊,

乡愁是一方矮矮的坟墓,

我在外头,

母亲在里头。

而现在,

乡愁是一湾浅浅的海峡,

我在这头,

大陆在那头。

——余光中《乡愁》

本文为读 lodash 源码的第三篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodash

gitbook也会同步仓库的更新,gitbook地址:pocket-lodash

作用与用法

compact 函数用来去除数组中的假值,并返回由不为假值元素组成的新数组。

falsenull0""undefinedNaN 都为假值。

例如:

var arr = [1,false,2,null,3,0,4,NaN,5,undefined]
_.compact(arr) // 返回 [1,2,3,4,5]

源码

function compact(array) {let resIndex = 0const 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 有两个致命的特性:

  1. for...in 的遍历不能保证顺序
  2. 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,foofor...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 = 0const _self = thisreturn {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 = 0while (index < this.length) {yield this[index++] * 2   }
}

因此在不改写 Symbol.iterator 的情况下,使用 for...of 来遍历数组是安全的,因为这个方法是数组的原生方法。

关于 IteratorGenerator 可以点击参考中的链接详细查看。

参考

  1. MDN:迭代器和生成器
  2. Iterator 和 for...of 循环
  3. Generator 函数的语法
  4. Lodash源码讲解(3)-compact函数
  5. MDN:for...of
  6. MDN:for…in

License

署名-非商业性使用-禁止演绎 4.0 国际 (CC BY-NC-ND 4.0)

最后,所有文章都会同步发送到微信公众号上,欢迎关注,欢迎提意见: qrcode_front-end-article.jpg

作者:对角另一面

转载于:https://www.cnblogs.com/hefty/p/8055931.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/251284.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[HAOI2008]移动玩具

这又是一道神奇的搜索题。。。只要记录每种状态。。。然后暴力判断这种状态往后一步的情况。。。 广搜出最优解即可。。。 呆码&#xff1a; #include<iostream> #include<cstdio> #include<queue> #include<cstring> using namespace std;int dx[5]{0…

javascript --- Sortable一个拖拽的接口

最近项目里面要实现需要实现一个拖拽功能,自己实现很麻烦,就在网上找到了一个封装好的sortable函数,github(https://github.com/SortableJS/Sortable). 首先是依赖的引入:(也可以自己下载源代码导入,方式很多呢) <body><!-- Latest compiled and minified CSS -->…

个人发展战略6天课

第一课 冰山模型 【知道自己为什么会不喜欢一份工作】 衡量和一个岗位是否匹配、匹配程度如何 从上到下的要素 知识技能&#xff08;30%&#xff09;特定领域 - 可以后天学习的&#xff0c;显性的、容易看出来的 和工作的关系&#xff1a; 工作中有陌生的内容&#xff0c;每天…

《Web全栈工程师的自我修养》

1. 什么是全栈工程师 Full-Stack Engineer Facaebook只招全栈工程师&#xff1f; Web开发流程 产品经理-->交互设计-->视觉设计-->开发&#xff08;前端、后端&#xff09;-->测试-->发布 流水线的优势 “各司其职”的弊端 工程师职责不清导致效率低工程师缺乏主…

div设置overflow-scroll滚动之后,jq获取其子元素的offset.top出现问题。

先上个图&#xff1a; 布局很简单&#xff0c;左右超过屏幕的部分自行滚动。 1. html <div class"ce-container"><div class"ce-leftBox">//左边的内容</div><div class"ce-rightBox">//右边的内容 </div…

javascript --- 利用Sortable实现一个可视化公式编辑器

Sortable的依赖引入和简单使用参见:https://blog.csdn.net/piano9425/article/details/90437182 先简单的介绍一下可视化公式编辑器的功能(样式没有调,毕竟每个人的需求都不一样): 首先会有2个需要计算的(我称为操作数)A_XiangRaoZuWenSheng和AYKZQ_CS,以及两个操作符ADD(加法…

使用uni-app搭建微信小程序

0 问题待解决 import { } 与否为什么要封装对齐问题每次重启项目&#xff0c;sitemap就会消失动态修改标题失效图片问题多多 &#xff1a;高度自适应 改成image固定高度&#xff0c;mode&#xff1a;aspectFill微信小程序文档b站视频链接后端接口文档 一、知识点 uni-app 是…

HttpTomcat

本节内容&#xff1a; Http协议Tomcat服务器下面开始的一系列内容都是JavaEE的内容&#xff0c;主要的内容见下图。JavaEE主要是做服务器端开发。 JavaEE全部规范&#xff1a;有13门技术&#xff0c;主要做web开发的需要学习servlet和jsp。其他技术&#xff0c;像EJB、 JNDI、J…

python---线程与进程

一 线程 1.1 概述 线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中&#xff0c;是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流&#xff0c;一个进程中可以并发多个线程&#xff0c;每条线程并行执行不同的任务。 Threading用于提供线程相关…

jquery常见操作分享

参考: https://www.cnblogs.com/eager/p/7133270.html

es 插件

类 若 实现NativeScriptFactory接口。A factory to create instances of either {link ExecutableScript} or {link SearchScript} 只是一个工厂类&#xff0c;仍需要 创建 上面二者之一。实际中 需 创建 类 继承 SearchScript接口的实现类AbstractSearchScript 的 子类 Abstra…

使用uni-app报错this.setData is not a function

一、报错 出现this.setData is not a function报错的绝大多数情况是&#xff0c;在回调时&#xff0c;this指向作用域为当前异步方法所在的对象&#xff0c;详见微信小程序异常处理——this.setData is not a function报错处理 二、排查原因 但我并没有在回调中使用this.set…

搭建SpringBoot服务器,在公司内网中使用

搭建SpringBoot服务器&#xff0c;在公司内网中使用。 学习了&#xff1a;https://blog.csdn.net/z3881006/article/details/78902231 就是一个程序&#xff0c;托管于github&#xff1b;https://github.com/spring-io/initializr 转载于:https://www.cnblogs.com/stono/p/9301…

javascrpt --- 使用jquery添加dom元素和Angular ng-repeat生成select性能比较

今天用两种方法实现了动态的给select添加option的功能. 第一种是用jquery. // html <select id"drag-pointList"></select> // js $(#drag-pointList).children(option).remove(); // 清空之前的option let list res.data.list ; // res是ajax请…

任何时候,写下一个类,一定要有三个函数

1、默认构造函数 2、拷贝构造函数 3、虚 析构函数转载于:https://www.cnblogs.com/buddho/p/8076165.html

【C语言及程序设计】项目2-15:模块化的简单银行系统设计

问题描述&#xff1a; https://edu.csdn.net/course/play/456/4808 // 银行系统.cpp: 定义控制台应用程序的入口点。 //#include "stdafx.h" #include <stdlib.h> #pragma warning (disable: 4996)int PassTest(); void ibalance(); void withdraw(); void de…

jquery --- 使用when方法等待2个异步事件结束后执行某一个函数.

$.when(promise1, promise2) .done(function(args1, args2){console.log(args1 args2); }// 上述代码,等待promise1和promise2执行完,打印出(promise1和promise2)使用的参数 // 注:1.promise1和promise2是异步调用的函数,如ajax请求 // 2.如果执行promise1时用到了一个参数…

Android基础知识(一)

此篇文章开始逐步记录一些Android开发的一些相关知识。本文主要讲了一些adb的常用指令&#xff0c;Toast的常规使用&#xff0c;Intent的显式启动活动&#xff0c;按钮点击事件的四种处理方法&#xff0c;一些常见布局。 1. ADB常用指令 Android Debug Bridge adb android调试桥…

Android 6.0 动态权限申请

1. 概述 Android 6.0 (API 23) 之前应用的权限在安装时全部授予&#xff0c;运行时应用不再需要询问用户。在 Android 6.0 或更高版本对权限进行了分类&#xff0c;对某些涉及到用户隐私的权限可在运行时根据用户的需要动态授予。这样就不需要在安装时被强迫同意某些权限。 2. …

el-input输入金额,保留两位小数

需求&#xff1a;“只允许输入金额保留两位小数”&#xff0c;有2种实现方法 方法一&#xff08;通过正则控制&#xff09;&#xff1a; html&#xff1a; <el-inputv-model"inputTable.amount"input"formatNum(form.amount, amount)" ></el-i…