写点什么

JavaScript 概念 - 高阶函数

作者:yuanyxh
  • 2024-09-14
    中国香港
  • 本文字数:1852 字

    阅读完需:约 6 分钟

JavaScript 概念 - 高阶函数

简述

高阶函数,即对函数进行操作的函数,在 JavaScript 中是一个重要的概念,拥有多种运用,能够理解并合理使用高阶函数,才不失为一个合格的 JavaScript 开发人员。

概念

我们知道,在 JavaScript 中,函数 是一等公民,任何能够使用变量的地方也都能够使用函数代替,即把函数看作具体的值或数据,如


// 数组元素可以是函数const arr = [() => {}];
// Map Set 的键、值可以是函数// Map Set 是 ES6 新提出的数据类型const map = new Map();map.set( () => {}, () => {}); // 添加一个键、值都为函数的键值对
// 将函数作为参数传递function sayHi() { console.log('hi, nice to meet you!');}setTimeout(sayHi, 1000); // 1 秒后打印:hi, nice to meet you!
// 返回一个函数function createFunc() { return () => console.log('悠悠苍天,何薄于我');}const print = createFunc();print(); // 悠悠苍天,何薄于我
复制代码


可以看到,任何能够使用变量的地方都能够使用函数代替,而高阶函数则可以认为是接收函数作为参数或返回一个函数的函数。


JavaScript 中,处处可见高阶函数的身影,如上述示例中的 setTimeout 方法,以及数组的 mapreducefilter 等方法皆是高阶函数。

运用

说完了高阶函数的相关概念,我们再来说说关于高阶函数的运用,关于高阶函数的运用主要有以下几种

内部调用

最容易理解的一种,将函数交由高阶函数调用,在内部传递参数,如数组的 filter 方法


// filterconst arr = [2, 4, 6, 8, 10];const newArr = arr.filter((ele, index, arr) => ele > 5);console.log(newArr); // 6 8 10
复制代码


filter 方法对每个数组元素调用传入的回调方法,根据回调返回值决定是否舍弃数据,我们根据它的特性手动实现一个高阶函数 filter


// 手动实现 filterfunction filter(arr, callback, thisArgs) {  const newArr = [];
for (let index = 0; index < arr.length; index++) { const ele = arr[index];
const result = callback.call( thisArgs !== undefined ? thisArgs : this, ele, index, arr );
if (Boolean(result)) { newArr.push(ele); } }
return newArr;}
const arr = [2, 4, 6, 8, 10];const newArr = filter(arr, (ele, index, arr) => ele > 5);console.log(newArr); // 6 8 10
复制代码


可以看到效果是相同的。像很多官方 Api 和很多库的作者提供的一些高阶函数,隐藏了内部实现,让我们不知道它做了什么,只知道结果,但我们能够依据它的特性模拟出相似效果。

函数包装

顾名思义,函数包装就是在指定函数外层包装一层函数,有什么用呢?我们思考以下场景:


  • 多个函数都需要在调用前执行固有操作

  • 多个函数都需要传入固有参数


像以上场景,难道在每次函数调用时都手动书写一次吗?很明显不是,我们能够对指定场景进行一层函数的封装


// 接收一个函数并返回一个包装函数function createContainer(callback) {  return function (...args) {    let result = null;    // 开启一个进度条    progress.start();    try {      result = callback.apply(this, args);    } finally {      // 结束进度条      progress.stop();    }    return result;  };}
复制代码


上述代码定义了一个 createContainer 函数并返回一个包装函数,每次调用这个函数时会开启一个进度条,然后调用指定回调,不管结果如何都在回调执行完毕后关闭进度条。


代码只是示例,有关进度条的场景应该存在于异步任务中,但上述代码没有处理异步的逻辑。

函数柯里化与惰性求值

柯里化:指将多参数函数转换为单参数函数多次调用传参。


惰性求值:一般函数调用,都是立即执行并返回结果的,那如果有这样的需求呢:已有数据当作参数传入函数,但不希望立即得到结果,因为还需要其他数据,但其他数据还未获得,这时我们就可以利用函数柯里化进行数据的惰性求值。


function setName(name) {  return function (age) {    return { name, age };  };}
const getInfo = setName('yuanyxh');setTimeout(() => { const info = getInfo(22); console.log(info); // 两秒后: { name: 'yuanyxh', age: 22 }}, 2000);
复制代码


上述代码是模拟的场景,利用了函数柯里化,先调用 setName 并传入数据,返回一个函数,因为闭包的存在,此时参数 name 并没有被销毁,仍然能够被内部函数访问,在两秒后调用返回的内部函数再次传入数据,达到了惰性求值。

结语

本文理解并讲述了关于 JS 中高阶函数的概念及部分运用,内容有误请指正,内容有缺请补充。


发布于: 刚刚阅读数: 3
用户头像

yuanyxh

关注

站在巨人的肩膀上 2023-08-19 加入

web development

评论

发布
暂无评论
JavaScript 概念 - 高阶函数_js_yuanyxh_InfoQ写作社区