专栏首页亦枫的大佬之路JavaScript进阶之路系列(一): 高阶函数

JavaScript进阶之路系列(一): 高阶函数

看了这篇文章,你就会高阶函数了,是不是听起来很牛?高阶函数,听起来很高级,其实是很接地气,大家经常会用到的东西,比如filter,map,回调函数。

高阶函数是对其他函数进行操作的函数,可以将它们作为参数或通过返回它们。简单来说,高阶函数是一个函数,它接收函数作为参数或将函数作为输出返回。

在《javascript设计模式和开发实践》中是这样定义的: 1.函数可以作为参数被传递; 2.函数可以作为返回值输出。

举个例子:

function foo(f){
        if(f instanceof Function){
            f();
        }
    }
    foo(function(){
        alert("asdf");
    })

定义了一个foo函数,foo函数传入了一个函数作为参数,foo函数里面添加判断,参数如果是函数就执行该参数。这就是一个简单的高阶函数。

回调函数(callback)

这个基本上是我们非常常用的高阶函数了,我们可以来看一下回调函数的代码:

function greeting(name) {
  alert('Hello ' + name);
}

function processUserInput(callback) {
  var name = prompt('请输入你的名字。');
  callback(name);
}

processUserInput(greeting);

定义了一个greeting函数,又定义了一个processUserInput函数,之后我们又把greeting函数作为参数传到了processUserInput中,这个就符合了高阶函数中的第一个定义,作为参数被传递。

回调函数作为高阶函数中的一种,它是干什么的呢?

回调函数是指 使用者自己定义一个函数,实现这个函数的程序内容,然后把这个函数(入口地址)作为参数传入别人(或系统)的函数中,由别人(或系统)的函数在运行时来调用的函数。

这么说,可能听不明白。

callback 一词本来用于打电话。你可以打电话(call)给别人,也可以留下电话号码,让别人回电话(callback)。回调函数的意思就是,你写了一个函数,让别人来调用,就是回调函数。

来看一看异步的回调函数:

function ff(a, b, cbk) {
    setTimeout(() => {
        cbk(a + b);
    }, 3000);
}
function f(callback) {
    var x = 3, y = 4;
    var z = 0;
    callback(x, y, function (re) {
        z = re;
        console.log(z)
    });
    console.log("主函数")
    return z;
}
f(ff);

对比来看,回调与同步、异步并没有直接的联系,回调只是一种实现方式,既可以有同步回调,也可以有异步回调,还可以有事件处理回调和延迟函数回调,这些在我们工作中有很多的使用场景。

我们可以像使用变量一样使用函数,作为另一个函数的参数,在另一个函数中作为返回结果,在另一个函数中调用它。当我们作为参数传递一个回调函数给另一个函数时,我们只传递了这个函数的定义,并没有在参数中执行它。

当包含(调用)函数拥有了在参数中定义的回调函数后,它可以在任何时候调用(也就是回调)它。

这说明回调函数并不是立即执行,而是在包含函数的函数体内指定的位置“回调”它(形如其名)。

回调函数是闭包的。

当作为参数传递一个回调函数给另一个函数时,回调函数将在包含函数函数体内的某个位置被执行,就像回调函数在包含函数的函数体内定义一样。闭包函数可以访问包含函数的作用域,所以,回调函数可以访问包含函数的变量,甚至是全局变量。

什么时候用回调函数?

我一般和别人合作项目的时候,想让人感觉我的代码写的很厉害,有时候会故意写两个回调,但是这种行为不可取的,减少代码冗余,无用的代码只会造成维护上的困难。

假如,你点击了一个按钮,你想让它执行连个函数,先执行函数A,再执行函数B,这时候就可以用回调函数了。

//定义主函数,回调函数作为参数
function A(callback) {
  callback();
  console.log('我是主函数');
}
//定义回调函数
function B(){
  setTimeout("console.log('我是回调函数')", 3000);//模仿耗时操作
}
//调用主函数,将函数B传进去
A(B);

这里有一个问题,我们执行A函数后,执行B函数,为什么不直接在A函数里调用,要传参过去呢?

function A(callback) {
 B();
  console.log('我是主函数');
}

function B(){
  setTimeout("console.log('我是回调函数')", 3000);//模仿耗时操作
}

这段代码的作用和回调函数好像是一样的。其实这两种方法在性能上是没有区别的,只是在灵活性上有很大的区别。 例如,我定义了一个C函数为回调函数。

//定义主函数,回调函数作为参数
function A(callback) {
  callback();
  console.log('我是主函数');
}
//定义回调函数
function B(){
  setTimeout("console.log('我是回调函数')", 3000);//模仿耗时操作
}
//调用主函数,将函数B传进去
function C(){
  setTimeout("console.log('我是回调函数')", 3000);//模仿耗时操作
}
//调用主函数,将函数C传进去
A(B);
A(C);

这时候,在试一下这段代码,就会出现很大的分歧。

function A(callback) {
 B();
 C();
  console.log('我是主函数');
}

function B(){
  setTimeout("console.log('我是回调函数')", 3000);//模仿耗时操作
}
function C(){
  setTimeout("console.log('我是回调函数')", 3000);//模仿耗时操作
}

过滤器(filter)

这是另一种高阶函数,老样子,先看一下代码。

 array.filter(function(currentValue,index,arr), thisValue)

filter 接收一个函数作为参数,所以它也符合高阶函数中的第一个定义,作为参数被传递。

那什么是filter呢? filter()方法会创建一个新数组,原数组的每个元素传入回调函数中,回调函数中有return返回值,若返回值为true,这个元素保存到新数组中;若返回值为false,则该元素不保存到新数组中;原数组不发生改变。

简单来说,filter就是一个过滤数组的方法,符合条件的被传入新的数组,不符合条件的,就不管它了。

举个例子:

在一个Array中,删掉偶数,只保留奇数,可以这么写:

 var arr = [1, 2, 4, 5, 6, 9, 10, 15];
 var r = arr.filter(function (x) {
     return x % 2 !== 0;
 });
 r; // [1, 5, 9, 15]

定义了arr这个数组,arr数组使用的过滤器,过滤器中函数的作用就是把数组中的偶数过滤出来,放进r数组中。

使用filter,注意两点,1.filter() 不会对空数组进行检测;2. filter() 不会改变原始数组。

过滤器很强大,比如,我们面试经常会考的数组去重:

'use strict';
 
 var r,arr = ['apple', 'strawberry', 'banana', 'pear', 'apple', 'orange', 'orange', 'strawberry'];
   r = arr.filter(function (element, index, self) {
      return self.indexOf(element) === index;
   });
 console.log(r.toString()); //apple,strawberry,banana,pear,orange

map

map()方法定义在JavaScript的Array中,它返回一个新的数组,数组中的元素为原始数组调用函数处理后的值。

注意: 1.map()不会对空数组进行检测;2.map()不会改变原始数组。

来看一看map的示例:

function pow(x){
    return x*x;
}
var arr = [1,2,3,4,5,6];
var results = arr.map(pow);
console.log(results);      //[1,4,9,16,25,36]

定义了一个pow函数,函数的做用是让参数平方,map函数的参数是pow参数,map遍历了数组,把数组的每一项传进了pow函数里面,再return出来。

map也是一个典型的高阶函数。

总结:

高阶函数就只讲这两个方法了,我相信以各位的聪明才智已经理解了什么是高阶函数。

最后留一个小思考,闭包,是不是高阶函数呢?

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • JavaScript中的闭包(closure)

    在JavaScript中,当一个内部函数被其外部函数之外的变量引用时,就形成了一个闭包。简单说,闭包就是能够读取其他函数内部变量的函数。

    刘亦枫
  • JavaScript进阶之路系列(三):节流防抖

    问题提出: 假如你要提交一个表单,你点击了按钮,出发了提交操作。这时候,你的网络不太好,提交的请求还没得到返回的时候,你又点击了一次按钮,提交了两次,怎么办,...

    刘亦枫
  • JavaScript中的函数防抖与函数节流

    函数防抖(debounce)当调用动作过n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行时间。

    刘亦枫
  • 深入理解 JavaScript 回调函数 [每日前端夜话0xDF]

    JavaScript 回调函数是成为一名成功的 JavaScript 开发人员必须要了解的一个重要概念。但是我相信,在阅读本文之后,你将能够克服以前使用回调方法...

    疯狂的技术宅
  • 第六章 函数

    函数(有些编程语言中叫做方法)几乎是每个编程语言最重要的内容,无论是函数式编程还是面向对象编程。每一段程序都由大量的函数构成,比如内置函数或者自定义函数。可以说...

    宇宙之一粟
  • 【玩转腾讯云】万物皆可Serverless之关于云函数冷热启动那些事儿

    然后我们再来看一下腾讯云云函数文档里的简介 https://cloud.tencent.com/document/product/583/9199

    乂乂又又
  • Matlab 自定义函数初讲

    在Matalb中使用函数可以把一个较大的任务分解为多个较小的任务,我这个Big project就是由个数多到我都不愿意看的一个个子函数构成的。每个子函数完成特定...

    matlab爱好者
  • Python 3 函数

    函数能提高应用的模块性,和代码的重复利用率Python提供了许多内建函数,比如print()。但也可以自己创建函数,这被叫做用户自定义函数。

    用户6184845
  • Python进阶教程笔记(七)函数式编程

    由于参数 x, y 和 f 都可以任意传入,如果 f 传入其他函数,就可以得到不同的返回值。

    Lemon黄
  • kotlin Standard中的内联函数示例详解

    let、with、run、apply、also、takeIf、takeUnless、repeat函数的使用

    砸漏

扫码关注云+社区

领取腾讯云代金券