Javscript 高阶函数(上)

栏目: 编程语言 · 发布时间: 5年前

内容简介:对于ES6的语法的引入,很多时候是我所不理解的,甚至是知道用法(api),却不清楚何使用、为什么用。一次偶然的机会,在Youtube看到了一个系列教程,觉得非常不错,遂记笔记,进行详细的梳理和整合,非常推荐大家去看原版啦,老师是个10年的coder,讲课比小品还生动的(@fun fun function)因为一次可能更新不完,之后有新的内容会更新在github,欢迎小伙伴star,共同进步.从一段程序开始

对于ES6的语法的引入,很多时候是我所不理解的,甚至是知道用法(api),却不清楚何使用、为什么用。一次偶然的机会,在Youtube看到了一个系列教程,觉得非常不错,遂记笔记,进行详细的梳理和整合,非常推荐大家去看原版啦,老师是个10年的coder,讲课比小品还生动的(@fun fun function)

因为一次可能更新不完,之后有新的内容会更新在github,欢迎小伙伴star,共同进步. 传送门

filter

从一段程序开始

var tripe = function(x) {
    return x*3;
}

var waffle = tripe;
waffle(3);
复制代码

这是一段简单到不能再简单的代码了,但是,这也是JS的特殊之处。一切皆对象,函数也是对象,函数可以是一个变量(var)。

正式这样的特殊性,让函数可以作为另一个函数的参数(callback)进行传递,这种方式就是高阶函数--函数中包含着另一个函数。下面就从 filter 函数举例来说

现在有一个需求,要找到一个数组中比3大的数字,并拿出来。

先看传统的,不用filter的方案

function big(arr) {
    let ret = [];
    for(let i = 0; i<arr.length; i++) {
        if arr[i] > 3
        ret.push(arr[i]);
    }
    return ret;
}
// 如果现在需求改成找比3小的?再写代码?而且没有复用性。
复制代码

好了,有没有可能简化或者优化下代码呢?让它更具有可维护性,更清楚点? 来看看最简单的函数式编程,使用filter的例子。

var arr = [1,2,3,4];
var b = arr.filter((x)=>{return x >3 });
console.log(b)  // [4]
复制代码

好了,可以看到filter接收一个函数作为参数,函数会遍历数组每一个item,当 returntrue 时,会返回当前的 item

这只是一个简单的逻辑,我们说的函数式编程,目的是为了让函数尽量功能简单、逻辑清晰,没有耦合的组织可以让函数复用和代码高效上更有利。所以我们改写下上面的内容

var isBig = function(x) {
    return x > 3;
}
var b = arr.filter(isBig);
复制代码

这样子,就完成了解耦,我们就是把函数当作一个变量、参数传给了另一个函数,这就是最基本的思想。 这样做的好处是什么?比如我们现在需要找到比3小的数字,只需要一行代码

var c = arr.reject(isBig);
复制代码

是的,就是这么简单,是不是很神奇? 想想传统的思路,找比3大和比3小是怎么做的?

map

map 是映射的意思,其实和filter很相似,只不过map是映射和变换原数组。什么意思?看下面的需求

一个数组中包含了很多对象,我想要获取这些对象的名字

var animals = [
    {name:'cindy', species:'dog'},
    {name:'hash', species:'duck'},
    {name:'gigi', species:'rabit'},
    {name:'chik', species:'cat'},
]
复制代码

好了,传统的做法又是要循环、遍历然后push之后,返回了。用map就很简单

var name = animals.map(obj=>{
    return obj.name;
})
复制代码

注意,这就是map和filter的不同: filter遍历,并根据 trueflase 决定是否返回原对象(item); map就是纯粹的变换(transform)了。

当然,我们可以利用map,让内容变的更加丰富

var name = animals.map(obj=>{
    return `${obj.name}是一个${obj.species}`
})
复制代码

reduce

reduce最基本的用法如下:

var arr = [1,2,3,4,5];
var sum = arr.reduce((sum,next)=>{
    return sum + next;
},0)
复制代码

reduce接收两个参数,第一个是 callback ,第二个参数是 初始值arr.reduce(callback(p1,p2),init)

为什么要初始值呢?注意一下回调函数中,是两个参数,p1参数是上一次循环(loop)的返回结果,p2参数是当前遍历到的(item)。 很显然,那么第一次遍历时的p1,就是由 init 提供的。

reduce绝不仅仅是遍历一个数组或者做一个加法这么简单。他可以做更复杂事情,可以对对象进行操作。 比如我们看到这个例子:

var name = [
    'sam\tblender\t200\t1    ',
    'sam\tpot\t130\t5    ',
    'nacy\tconaver\t20\t3    ',
    'amy\tpot\t130\t2    ',
    'amy\tblender\t4\t2    '
]
复制代码

现在需要统计这个数组中,sam\nacy\amy的个人财产和数据。 怎么办呢? 利用上面介绍的方法组合,可以很好的做到

思路:

  1. map对每个字符串进行处理和变换
  2. 使用reduce进行统计
var output = 
    name.map((item)=>{item.trim().split('\t')})
        .reduce((custormers,line)=>{
           custormers[line[0]] = custormers[line[0]] || [];
           custormers.push({
               name: line[0],
               property: line[1],
               price:line[2]
               quilty: line[3]
           })
        },{})

复制代码

嗯,可能有点复杂了,一行一行来看。

map的作用: 经过map,将原来每一项的字符串,转换成了数组,现在是
这样的形式:
[
    [[sam],[blender],[200],[1]],
    [[sam],[pot],[130],[5]],
    ...
]

好了,继续就到了reduce。我们给reduce穿的`init`是一个空对象,也就是
会创建一个对象作为返回值
第一次循环: 
  custormers:{}  line: [[sam],[blender],[200],[1]]
  custormers.sam = [];
  custormers.sam.push({...})
     ==> 最终 
     custormers = {
        sam:[{
            name:sam,
            property:blender,
            price:200,
            quilty:1
        }]
     }

第二次循环:
    custormers:{sam:[...]}  line:[[sam],[pot],[130],[5]]
    custormers.sam = [...]
    继续为sam这个对象,增加“财产"
    ...

...

复制代码

所以看到了,这就是reduce的威力。

reduce 实现compose

好了,reduce的作用可能远不止于前面介绍的那些,因为至少我们在菜鸟教程上看的话,会很醒目的告诉你,reduce可以实现 函数compose

Javscript 高阶函数(上)

我擦,这是什么东西?赶快了解了一下

什么是compose

compose就是执行一系列的任务(函数),比如有以下任务队列

let tasks = [step1, step2, step3, step4]
复制代码

每一个step都是一个步骤,按照步骤一步一步的执行到结尾,这就是一个compose compose在函数式编程中是一个很重要的 工具 函数,在这里实现的compose有三点说明

  • 第一个函数是多元的(接受多个参数),后面的函数都是单元的(接受一个参数)
  • 执行顺序的自右向左的
  • 所有函数的执行都是同步的

面向过程的实现

使用递归的思想,不断的检测任务队列中是否有任务,如果有任务就执行,并把执行结果传递. 实现过程如下

var compose = function(args) {
    let length = args.length;
    let count = lenth - 1;
    let result;
    return function inner(...inArgs) {
        result = args[count].apply(null,inArgs);
        if(coun <= 0) {
            return result;
        } 
        count--;
        return args[count].apply(null,result);
    }
}
复制代码

generator实现

generator是会专门写一个介绍的(就在近期更新),同样是看了很多内容后,理解了它才做的。 因为generator本身就是实现中断、处理、再中断的流程。 generator遇到yield就会抛出一个iterator,而其next()方法可以传递参数,就可以实现传值,将上一步的运行结果,作为下一步的参数。 好了,下面就generator来实现。

function * iterateSteps(steps) {
    let param;
    steps.forEach((step)=> {
        if(param) {
            param = yield step.apply(null,param)
        } else {
            param = yield;
        }
    })
}

const compose = function(steps) {
    let g = iterateSteps(steps);  //g是一个generator,通过next()执行
    return function(...args) {
        let val = steps.pop().apply(null,args) //val返回的值作为将作为第二个函数的参数
        //第一个函数无法传参,因此空消耗一个yield
        g.next()
        return steps.reverse.reduce((result,fn)=>{
            g.next(result).value //返回
        },val)
    }
}
复制代码

解释:

这个可能看起来就有点让人迷糊了。 首先,generator就是一步步来运行函数的,只不过,与普通函数不同,运行到函数的地方,它会把"线程"抛出来,让外面来进行解决。当下次迭代到 next(val) 的时候,会把val值传回。

首先,因为第一次的运行无法传参数,所以我们将param定义,但是不赋值。 在 if-else 中,第一次将走到else,运行 param = yield 。 所以,当第一次运行next的时候,实际只是为了消耗yield。

在compose函数中,首先运行iterataSteps,接下来使用reduce来 自动运行 这个generator,就可以使实现compose了。注意这里 Steps.pop.apply(null,args) 是弹出了最后一个函数,并执行,拿到的值,作为 reduceinit 参数。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

深入浅出Struts 2

深入浅出Struts 2

Budi Kuniawan / 杨涛、王建桥、杨晓云 / 人民邮电出版社 / 2009-04 / 59.00元

本书是广受赞誉的Struts 2优秀教程,它全面而深入地阐述了Struts 2的各个特性,并指导开发人员如何根据遇到的问题对症下药,选择使用最合适的特性。作者处处从实战出发,在丰富的示例中直观地探讨了许多实用的技术,如数据类型转换、文件上传和下载、提高Struts 2应用的安全性、调试与性能分析、FreeMarker、Velocity、Ajax,等等。跟随作者一道深入Struts 2,聆听大量来之......一起来看看 《深入浅出Struts 2》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

SHA 加密
SHA 加密

SHA 加密工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具