好久不见的每周装逼,今天又回来了!

707 阅读8分钟

因为这段时间公司的KPI压力,已经有一段时间没发过每周装逼了,并且每周算法题也拖了2周了。今天就拿个简单的算法题,顺便装装逼了

再简单聊聊函数式编程

每周算法题

这次就先不拿LeetCode的题来倒腾了,今天我们要讨论的题目是:如何在移除数组中简单的移除无效的值

先说说我们在这里无效的值指的是什么,无效的值包括falsenull0""undefinedNaN

有时候咱们在做数据处理的时候,这些内容都是属于脏数据,这些脏数据要剔除掉不展示给用户,或者并不需要存储这些值

那我们如何高效的清除掉这些数据呢?

先展示一个案例,让我们更深刻的理解一下我们要做的事情

传入数组: 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是函数式编程中最常用的一个操作之一,其余还有mapreduce,先简单聊聊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语言的时候走了不少弯路,在我有好些门别的语言基础下,还花了好几个晚上摸索入门,想想都觉得浪费时间了-,-,所以趁着假期,刚好也有时间了,就给你们整理整理。

妇联四我怕是没时间去看了哦~~~

扫码关注微信公众号「闹闹吃鱼」,领取大礼包!内容都是经过我本人筛选,整理后的好资源,不仅仅只是技术~!