因为这段时间公司的KPI压力,已经有一段时间没发过每周装逼了,并且每周算法题也拖了2周了。今天就拿个简单的算法题,顺便装装逼了
再简单聊聊函数式编程
每周算法题
这次就先不拿LeetCode的题来倒腾了,今天我们要讨论的题目是:如何在移除数组中简单的移除无效的值
先说说我们在这里无效的值指的是什么,无效的值包括false
,null
,0
,""
,undefined
,NaN
有时候咱们在做数据处理的时候,这些内容都是属于脏数据,这些脏数据要剔除掉不展示给用户,或者并不需要存储这些值
那我们如何高效的清除掉这些数据呢?
先展示一个案例,让我们更深刻的理解一下我们要做的事情
传入数组: arr([7, "ate", "", false, 9]) 变成:[7, "ate", 9]
传入数组: arr(["a", "b", "c"]) 变成:["a", "b", "c"]
传入数组: arr([false, null, 0, NaN, undefined, ""]) 变成:[]
传入数组: arr([1, null, NaN, 2, undefined]) 变成:[1, 2]
熟悉JavaScript类型转换的你们,都知道上面那些无效的值,如果转换成Boolean类型后,那些无效的值,结果都会是false
所以我们只要移除数组元素在转换成Boolean
类型后,值为false
的的元素就好了
这还不简单?轻轻松松就能写出如下的Code
let arr = [7, "ate", "", false, 9, NaN];
function convert(arr) {
for (let i = 0; i < arr.length; i++) {
const element = arr[i];
if (Boolean(element) === false) {// 如果值为false
arr.splice(i, 1); // 删除下标索引为i 的元素
i--;
}
}
return arr;
}
convert(arr) // [7, "ate", 9]
要是这Code写成这样就够了,那还装啥B啊?还要不要脸了?
使用Filter
Filter
是函数式编程中最常用的一个操作之一,其余还有map
和reduce
,先简单聊聊Filter
是做什么的
简单来说,Filter
帮我们遍历了数组中的每一个元素,省去了我们写for
循环,它还有个回调函数供我们使用,用于操作数组
Filter
的语法也比较简单,如下所示
var NewArr = array.filter(callback(element,index,array),thisArg)
这个回调函数callback
有三个参数供我们使用,分别是元素值、元素索引、以及调用了 filter 的数组本身
后面的thisArg
参数是我们在Filter
里执行callback
使用this
方法时,this
指向的数组
当我们的回调函数只使用一个参数的时候,默认使用的是数组元素值,如果回调函数使用两个参数的时候,分别就是数组的元素值与元素索引。那如果是三个参数……自己脑补吧…换句话说,除了第一个参数element
之外,其它的参数都是可选的
执行完Filter
最后会返回一个新的数组,并不是原来的数组
好了,简单的介绍了一下Filter
的原理以及使用方式后,那我们来看看这次实战怎么利用Filter
来帮我们更简洁并且支持高并发的处理
那我们先将上面的convert
函数改写成使用Filter
看看是怎样一种形式
let arr = [7, "ate", "", false, 9, NaN];
function convertByFilter(element) {
return Boolean(element) === true; // 如果传入的参数为false,则不返回
}
arr.filter(convertByFilter) // 将不合规的值筛除,结果为: [7, "ate", 9]
是不是感觉比之前的convert
要牛逼多了?不过既然要装逼,上面的处理方式明显还不够装逼的
convertByFilter
函数可以改写成
let arr = [7, "ate", "", false, 9, NaN];
arr.filter(Boolean);// 结果为: [7, "ate", 9]
哈哈哈,虽然效果是一样的,但是B格明显就不在一个层次了,因为Boolean也是一个函数,看看上面那个Boolean(element)===true
就明白了
如果你想对数组进行别的筛选操作,那把Boolean
函数直接替换成你想实现的功能就好了~
函数式编程
今天就简单说说函数式编程
函数式编程有两个最大的特点,分别是无状态与不可变
不可变相对于无状态来说,还是非常好理解的,就是不管在函数中怎么操作传入的参数,外部的值绝对是不能被改变的
倒是无状态很是让人纠结凌乱,老读者应该都知道,我这人最烦的就是各种专业术语,能用白话说清楚的东西,就绝对不说术语
先给大家举个例子,看完例子后,咱们再来说术语解释,这样就能很好理解什么是无状态了
先说个有状态的例子:
let count = 0;
function add(x) {
count += 1;
return count + x;
}
let sum = 0;
sum = add(1) + add(1);
console.log(sum);// 5
可以在这思考一下,为什么上面的结果是5
,咱们再来看看无状态的function
是长什么样子的
let sum = 0;
function nonState(x) {
return x + 1;
}
sum = nonState(1) + nonState(1);
console.log(sum);// 4
不知道大家有没有看出来区别,第一个函数add
,在函数内部处理了自己函数外的变量,当我们执行两次add(1)
,第一次add(1)
得到的结果是2
,而第二次执行add(1)
的结果却是3
像无状态的函数nonState
,不管执行nonState(1)
多少次,它最后得到的结果都是2
有些人可能会说,那我不改变外部变量不就行了?我在函数add
内部用个变量a
把外部变量count
复制一下,不就可以做到不改变外部变量,这不就变成没状态了么?
小伙子很有想法嘛,但是只要外部的count
发生了改变,那么函数内部你用来复制count
的变量a
的值也会改变,所以依然是被称为有状态~
而且你能保证在整个项目中,count
这个全局变量不会在别的地方被操作?
如果我在别的函数里把count+1
,那你再调用add(1)
的时候,结果不还是变成3
了么?
在异步程序或者多线程程序中,你本是想先执行add(1)
再去count++
,可能一不小心就变成了count++++++++++++++
之后再执行add(1)
,而且因为依赖了外部变量count
,当你把写好的函数要复制到别的js文件内时,很有可能就会用不了哦~~
真是没有状态就没有伤害啊~~~
正因为函数式编程不牵扯到外部的变量,再加上不会改变输入的参数,所以在支持高并发的同时,还极大程度的方便了我这种CV码畜
又因为函数式编程,咱们写的这个函数没有外部变量的依赖,所以不管把它Copy到哪里,它都不会受到影响。CV走起来麻麻再也不担心我CV后因为Bug掉头发了
函数式编程的缺点
说一个方法的好却不说它的坏,这简直就是耍流氓,那函数式编程又有什么缺点呢?
这个缺点就是数据复制非常严重,因为每调用一次这个函数,传入的参数都会被复制一份,换句话说,就是对内存消耗比较多
复制个小变量什么的倒是问题不大,毕竟有时候咱们会传个字符串模板去处理字符串什么的,这就逆天了。像我有时候处理一个长度过万的字符串,那这要是在千万级并发下,Copy个几千万次,这内存……emmmm,突然发现我公司做ToB业务,没这么多并发,真香
其实,这个劣势也算不上什么大的劣势,因为函数式编程没状态,也没有外部依赖,所以并行执行的时候根本不需要考虑锁机制,我们拼了命的并发,都不用考虑死锁,不用担心会把程序跑死(故意作死除外,比如我上面说的上万长度的字符串例子)
写在最后
Javascript实现函数式编程还是比较简单的,毕竟JavaScript的函数传参默认都是值传递,函数内会把传入进来的参数值给复制一份,在函数内对传入参数的操作都是对复制后的副本进行的操作,所以对于函数式编程的第二个特征不可变
倒是不需要怎么刻意的去关注
总结来说,函数式编程两大特性也是两大优点,分别是无状态,不可变,缺点就是数据复制严重,希望今天的Filter
使用以及后面函数式编程说明,能让你理解什么是函数式编程
还有啊,这因为五一放假,周末还上班,所以今天周一推送了,周二说不定就不推了~~~
五一的假期的时候,我得把手头上的资源整理整理,到时候送给你们,并且我还答应某个小鸡贼,五一的时候推一篇「一天入门Go语言」,让你们快速的学习Go语言。
毕竟当初我学Go语言的时候走了不少弯路,在我有好些门别的语言基础下,还花了好几个晚上摸索入门,想想都觉得浪费时间了-,-,所以趁着假期,刚好也有时间了,就给你们整理整理。
妇联四我怕是没时间去看了哦~~~
网友评论