Javscript 高阶函数(上)

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

内容简介:对于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 参数。


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

查看所有标签

猜你喜欢:

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

Approximation Algorithms

Approximation Algorithms

Vijay V. Vazirani / Springer / 2001-07-02 / USD 54.95

'This book covers the dominant theoretical approaches to the approximate solution of hard combinatorial optimization and enumeration problems. It contains elegant combinatorial theory, useful and inte......一起来看看 《Approximation Algorithms》 这本书的介绍吧!

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

在线压缩/解压 HTML 代码

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

SHA 加密
SHA 加密

SHA 加密工具