【基于 JS 函数式编程 - 2】高阶函数 | 函数代替数据传递 | 函数是一等公民 | 闭包 | 使用高阶函数实现抽象 | 数组的高阶函数

优雅而高效的JavaScript——高阶函数 JavaScript是一种高级编程语言,它支持高阶函数。高阶函数是指可以接受一个或多个函数作为参数,或者返回一个函数的函数。在JavaScript中,函数是一等公民,这意味着函数可以像变量一样被传递、赋值和使用。这个特点使得JavaScript非常适合使用高阶函数。在本文中,我们将学习什么是高阶函数,以及如何在JavaScript中使用它们。我们还将探讨高阶函数的一些常见用途,如函数组合、柯里化和延迟执行。最后,我们将介绍一些常见的高阶函数,如map、filter和reduce。 阅读详情

⭐️ 本文首发自 前端修罗场(点击即可加入),一个专注 Web 技术、答疑解惑、面试辅导、职业发展的社区。

相关文章


函数作为参数传递是js规范中的一部分。而,
允许以函数代替数据传递是一个值得关注的概念。

我们把接受函数作为其参数的函数称为高阶函数(HOC)。

理解函数代替数据传递

函数是一等公民

我们知道,js支持以下几种数据:

  • Number
  • String
  • Boolean
  • Object
  • null
  • undefined

但是,值得注意的是,函数也可以作为js的一种数据类型!所以,在js中与对String和Number类型的操作类似,我们可以把函数存入一个变量等方式进行传递。
那么,当一门语言允许函数作为任何其他数据类型使用时,函数就被称为“一等公民”。即,函数可被赋值给变量,作为参数传递,也可被其他函数返回。

(❤️ 现在关注【前端修罗场】,后台回复【666】,即可获取一份【免费的优质学习资料】,一起学习,一起进步~)

简言之,在js中我们就把 函数 理解为 数据 !
既然它是数据,就可以把它存入一个变量,如:

let fn = () =>{} //fn是一个指向函数类型的变量 ,fn是函数的引用
fn(); //调用,指向fn指向的函数
  • 那么,具体是怎么“传递函数”呢?
    我们看下面这段示例代码:
let fn = () => {console.log('本函数fn作为作为数据进行传递')}
let tellType = (arg) => {
	if(typeof arg === "function"){arg();}
	else {console.log('传递过来的数据是'+ arg)}
}
tellType(fn); //输出 :本函数fn作为作为数据进行传递
  • 那,怎么返回函数呢?
let fn = () => {return String}
fn(); //String() { [native code ]} //返回一个指向String函数的函数引用(fn值返回函数引用,并没有调用。)
fn()("abc"); //调用

由此可见,高阶函数式接受函数作为参数,并且/或者返回函数作为输出的函数。

闭包

高阶函数的运行机制,得益于js中的闭包。

什么是闭包

简言之,闭包是一个内部函数(即,是在另一个函数内部的函数)。

例如:

functioin outer() {
	function inner() {} //inner()就是一个闭包函数
}

之所以闭包函数强大,在于它对作用域链/层级的访问。
通常,闭包有3个可访问的作用域:

  • 1、在闭包函数,自身之内声明的变量,如:
function outer() {
	function inner() { 
		let a = 5;
		console.log(a); 
	}
	inner();//调用inner函数,输出5。
}

值得注意的是:inner函数在outer函数外部是不可见的!

  • 2、对全局变量的访问,如:
let global = 'global';
function outer() {
	function inner() { 
		let a = 5;
		console.log(global); 
	}
	inner();//调用inner函数,输出global。
}
  • 3、对外部函数变量的访问(关键),该性质使得闭包函数变得非常强大!
    如:
let global = 'global';
function outer() {
	let outer = "outer";
	function inner() { 
		let a = 5;
		console.log(outer); 
	}
	inner();//调用inner函数,输出outer。
}

由此,我们将外部函数成为包裹闭包函数的函数。

闭包可记住它的上下文

先看一个示例代码:

let fn = (arg) => {
	let outer = "visible";
	let innerFn = () => {
		console.log(outer);
		console.log(arg);
	}
	return innerFn;
}
let closureFn = fn(5);
//1、fn被参数5调用,返回innerFn。当innerFn被返回时,
js执行引擎将innerFn作为一个闭包,并相应地设置它的作用域。
//2、返回函数的引用存储在closureFn中。当closureFn通过作用域链被调用时就记住了arg、outer值!

closureFn();//输出: visible 5

抽象

高阶函数通常用在能够抽象通用的问题。即,高阶函数就是在定义抽象。

抽象:是一种管理计算机系统复杂性的技术。它通过建立一个人与系统进行交互的复杂程序,把更复杂的细节抑制在当前水平之下。程序员应该使用理想的界面,并且可以添加额外级别的功能,否则处理起来将会复杂!

即,抽象让我们专注于预定的目标,而无须关心底层的系统概念。

使用高阶函数实现抽象

例如:

const forEach = (array,fn) => {
	for(let i=0;array.length;i++){
			fn(array[i]) 
	}
}
forEach([1,2,3],(data)=>{
//data作为参数从forEach函数传到当前的函数
});
//检查数组的内容是否为一个数字、自定义对象或其他类型
const every = (arr,fn) => {
	let result = true;
	for(const value of arr)
		result = result && fn(value);
	return result;
}
every([NaN,NaN,3],isNaN);//检查给定的数字中是否有NaN类型的内容
//some函数:如果数组中的一个元素通过传入的函数返回true,some函数就返回true
const some = (arr,fn) => {
	let result = false;
	for(const value of arr) 
		result = result || fn(value);
	return result;
}
some([NaN,NaN,3],isNaN);//true
//unary
const unary = (fn) => {
	fn.length === 1 ? fn : (arg) => fn(arg)
}
let res = ['1','2','3'].map(unary(parseInt)) 
console.log(res); //输出 [1,2,3]
//once
const once = (fn) => {
	let done = false;
	return function() {
		return done ? undefined: ((dine=true),fn.apply(this,arguments))
	}
}
//声明一个名为done的变量,初始值为false。返回的函数会形成一个覆盖它的闭包作用域。因此,返回的函数会访问并检查done是否为true,如果是,则返回undefined,否则将done设为true,并用必要的参数调用函数fn。

  • 注:apply函数允许我们设置函数的上下文,并为给定的函数传递参数。
//memoized:使函数记住其计算结果
const memoized = (fn) => {
	const lookupTable = {};
	//返回函数将接受一个参数并检查它是否在lookupTable中,如果在,返回对应的值,否则使用新的输入作为key,fn的结果作为value,更新lookupTable对象
	return (arg) => lookupTable[arg] || (lookupTable[arg] = fn(arg));
}
let fastFactorial = memoized((n)=>{
	if(n===0){return 1;}
	return n* fastFactorial(n-1);
})

对数组有用的高阶函数

在js中,常需要对数组进行遍历。同时我们也使用数组进行存储、操作和查找以及转换数据格式等操作。

那么,在函数式编程中有哪些对数组有用的高阶函数呢?

map

forEach函数隐藏了遍历的通用问题,但是我们不能在所有的情况下都是用forEach。例如:假设把所有的数组内容都平方并在一个新的数组中返回。通过forEach要如何实现?forEach只能执行传入的函数,不能用来返回数据。
所以,这里我们想到map。

const map = (array,fn) => {
	let results = []
	for(const value of array)
		results.push(fn(value));
	return results;
}
map([1,2,3],(x)=>x*x); //[1,4,9]

filter

//筛选数组
const filter = (array,fn) => {
	let results = []
	for(const value of array)
		(fn(value)) ? results.push(value) : undefined;
	return results;
}
filter([{"rating":[4.5]},{"rating":[4.6]},{"rating":[3.5]}],(item)=>item.rating[0]>4.5); //[{"rating":[4.6]}]

concatAll

//把所有嵌套数组连接到一个数组中
const concatAll = (array,fn) => {
	let results = []
	for(const value of array)
		results.push.apply(results,value);
	return results;
}

reduce

  • 归约操作:对所有数组重复某个过程
//1、当initivalValue未定义时,我们从第二个元素开始循环数组,将它作为累加器的初始值。
//2、如果initivalValue由调用者传入,就需要遍历整个数组。
const reduce = (array,fn,initivalValue) => {
	let accumlator ;
	if(initivalValue !=undefined)
		accumlator = initivalValue;
	else 
		accumlator = array[0];
	if(initivalValue === undefined)
		for(let i=1;i<array.length;i++)
			accumlator = fn(accumlator,array[i])
	else
		for(const value of array) 
			accumlator = fn(accumlator,value)
	return [accumlator]
}
reduce([1,2,3],(acc,val)=>acc+val) //[6]
reduce([1,2,4],(acc,val)=>acc*val,1) //[8]

zip

  • 合并两个给定的数组
const zip = (leftArr,rightArr,fn) => {
	let index,results = [];
	for(index=0;index<Math.min(leftArr.length,rightArr.length);index++)
		results.push(fn(leftArr[index],rightArr[index]));
	return results;
}
zip([1,2,3],[4,5,6],(x,y)=>x+y); //[5,6,7]

在这里插入图片描述

汽车诊断之UDS入门-0x19 0x0A服务 0x19 0x0A解析 阅读详情

相关推荐

Android开发者必看:5分钟搞定DeepSeek API接入(附完整代码示例)

本文为Android开发者提供快速接入DeepSeek API的完整指南,包含环境配置、网络层设计、请求体构建等关键步骤,并附有详细代码示例。通过工程化实践,帮助开发者高效集成AI对话、代码生成等高级功能,提升移动应用竞争力。

bush的博客 535

一道有意思的题目

['1', '2', '3'].map(parseInt) // 输出[1, NaN, NaN] 延伸题: let unary = fn => val => fn(val) let parse = unary(parseInt) console.log(['1.1', '2', '0.3'].map(parse)) // [1, 2, 0] 若想把字符串数组转为数字数组: ['1', '2', '3'].map(parseFloat) // 输出[1, 2, 3] // p.

evalost的博客 339

西门子PLC新手必看:200SMART与1200/1500的MODBUS RTU通讯实战(附完整配置流程)

本文详细解析了西门子PLC中S7-200 SMART与S7-1200/1500之间实现MODBUS RTU通讯的完整实战流程。内容涵盖硬件接线、软件配置及程序编写,重点讲解了200SMART作为从站、1200/1500作为主站的典型架构,并提供了关键功能块的使用方法和常见问题排查技巧,帮助新手快速打通不同系列PLC间的数据交换。

weixin_29214559的博客 397

函数式编程之数组的函数式编程

5. 数组的函数式编程 在本章中,我们将创建一组用于数组的函数,并用函数式的方法而非命令式的方法来解决常见的问题 5.1 数组的函数式方法 本节将创建一组有用的函数,并用它们解决数组的常见问题 本节所创建的所有函数称为投影函数,把函数应用于一个值并创建一个新值的过程称为投影。讲个通俗的例子,forEach 没有返回值,所以就不是投影函数,map 有返回值,所以是投影函数 ...

zhang6223284的博客 917

ESP32开发环境与微信小程序通信的完整示例

通过搭建稳定的esp32开发环境,实现其与微信小程序间的双向通信,涵盖Wi-Fi连接、数据收发与协议设计,为物联网应用提供低成本解决方案。

weixin_42126677的博客 1132

从 JDK8 到 JDK17:CRMEB 框架升级

【摘要】CRMEB开源框架已成功从JDK8升级至JDK17,带来显著性能提升:并发吞吐量提高35%,响应延迟降低40%,资源占用减少25%。升级方案解决87个技术卡点,支持模块化扩展和微服务转型,兼容SpringBoot3.0等新技术。提供基础版到企业级的升级服务,包含代码重构指导和性能调优。经金融级验证的方案可帮助电商平台实现日均10万+UV承载,支付成功率提升至99.7%。现以低于市场30%的价格提供全周期技术支持,助力企业抢占技术先机。

weixin_39782540的博客 701

ES中数组的常用操作项

1、得到两个数组的交集 * @param {Array} arr1 * @param {Array} arr2 得到两个数组的交集, 两个数组的元素为数值或字符串 getIntersection = (arr1, arr2) => { let len = Math.min(arr1.length, arr2.length) let i = -1 let res = [] ...

渣渣飞的博客 3551

前端监控性能指标

文章目录前端监控性能指标性能指标阶段性指标关键性能指标小程序W3C Level 1兼容性常规用法注意事项1、计算HTML文档请求使用 Nav Timing2、计算静态资源使用 getEntriesByType('resource') 代替 getEntries()3、secureConnectionStart 问题4、跨域资源设置响应头 Timing-Allow-Origin5、注意属性值为 0 ...

foolbird 1348

['1', '2', '3'].map(parseInt) 解析

参考 MDN parseInt MDN map map语法 var new_array = arr.map(function callback(currentValue[, index[, array]]) { // Return element for new_array }[,thisArg]) callback 生成新数组元素的函数,使用三个参数: currentV...

ky1in93的博客 642

3DE重客户端安装

3DE重客户端安装

m0_48170265的博客 9429

【WorkBuddy专栏61】字节的AI打法为什么一直在变——WorkBuddy/CodeBuddy vs 字节Trae/TRAE Work/豆包全维度对比

【WorkBuddy专栏61】字节的AI打法为什么一直在变——WorkBuddy/CodeBuddy vs 字节Trae/TRAE Work/豆包全维度对比

AI人人懂 2757

echarts sunburst vue

vue页面 <template> <div class="echarts"> <div ref="dom" class="charts chart-bar" style="width:100%;height:100%;"></div> </div> </template> <script> import ...

adenfeng的专栏 792

Unreal从入门到精通之 UE4 vs UE5 VR性能优化实战

这篇文章分享了UE5 VR项目的性能优化经验。作者通过对比UE4和UE5的VR模板性能,发现UE5默认帧率只有45FPS,远低于UE4的100FPS。主要性能瓶颈来自Lumen全局光照、虚拟阴影贴图等新特性。文章详细列出了12项关键优化设置,包括禁用Lumen、关闭虚拟阴影、使用前向渲染等,并分析了各项调整对性能的影响程度。特别指出删除默认的VolumetricCloud可立即提升5帧。最后建议根据场景需求平衡画质与性能,对于高帧率要求的VR项目,可能需要谨慎使用UE5或关闭其部分高级渲染功能。

TxNet 1383

JS高级-函数一等公民

JS高级-函数一等公民

幽幽谷 1416

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

函数式编程 -- 函数是一等公民、高阶函数、闭包

函数式编程前言一、函数是一等公民1. 一等公民的定义2. 为什么说函数是一等公民二、高阶函数1. 什么是高阶函数总结 前言 学习函数式编程,首先要了解函数式编程相关的概念。 一、函数是一等公民 1. 一等公民的定义 根据维基百科,编程语言中一等公民的概念是由英国计算机学家Christopher Strachey提出来的,时间则早在上个世纪60年代。但是他并没有严格的给出一等公民的定义。 关于一等公民的定义,在《Programming Language Pragmatics》书中给出了答案。 In gene

zimeng303的博客 773

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

JavaScript函数式编程

函数式编程

pacong的博客 946

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

函数式编程与闭包

高阶函数(Higher-order function) — 学过react的话会觉得名字和其中的高阶组件一样,其实react的高阶组件本质上就是高阶函数可以把函数作为参数传递给另一个函数可以把函数作为另一个函数的返回结果函数作为参数// 高阶函数—函数作为参数// forEachi++){// filteri++){函数作为参数的好处。

2401_83916030的博客 795

函数式编程

什么是函数式编程? 函数式编程(Functional Programming,FP)是存函数的一种编程范式,是一种编写方式,和编写风格。 通俗的讲:函数式编程的概念来自数学中的函数的映射关系。一个函数的返回值,仅仅依赖于参数的值,而不会因为其他外部的状态不同而不同 例如:一个求幂的函数pow(base, exponent),它的计算结果仅仅依赖于base(基数),和exponent(指数)的不同而不同。这个函数无论我们调用多少次,只要参数一致,那么返回值绝对一致。 跟面向对象编程和面向过程编程是并

Start的小笔记 3392

【Javascript修炼篇】JS中的函数式编程

【Javascript修炼篇】JS中的函数式编程,如何在javascript中使用函数式编程

代码说,代码让世界更美好。 1916

JS 函数式编程

是

weixin_46261261的博客 2843

函数式编程与Js异步编程、手写Promise(01)

Part1 · JavaScript【深度剖析】 函数式编程与Js异步编程、手写Promise 文章说明:本专栏内容为本人参加【拉钩大前端高新训练营】的学习笔记以及思考总结,学徒之心,仅为分享。如若有误,请在评论区支出,如果您觉得专栏内容还不错,请点赞、关注、评论。 共同进步! 文章目录Part1 · JavaScript【深度剖析】函数式编程与Js异步编程、手写Promise@[toc]一、为什么要学习函数式编程二、什么是函数式编程三、前置知识1.函数是一等公民2.高阶函数什么是高阶函数使用高阶函数的

5coder的大前端进阶之路-个人博客网站:blog.5coder.cn 1806

JS中函数式编程详解版(FunctionalProgramming,FP)

函数式编程详解函数式编程的认识函数式编程前置知识函数式编程基础函子 函数式编程的认识 函数式编程是编程中的一种范式,与面向对象这种类型编程的风格(思想)是并列关系。 (编程范式: 编程思想或是说编程中的一种编程风格) 函数式编程:将现实世界中的事物与事物之间的 联系 抽象到程序世界(抽象其运算过程) 程序的本质:根据输入通过某种运算获得相应的输出,程序开发过程中会涉及很多有输入和输出的函数 函数式编程的函数是指数学中的函数例如:** y=sin(x)** ,x和y的映射关系 x->f(联系、映射

weixin_51457459的博客 3636

JavaScript 深度剖析 - 函数式编程范式

函数式编程范式1.1函数式编程概念1.2函数是一等公民1.3高阶函数1.5柯里化(Haskell Brooks Curry)1.6函数组合(compose)1.7Lodash中的fp模块1.8Point Free模式1.9Functor(函子)MayBe函子Either函子IO函子Task函子Pointed函子Monad(单子)总结 1.1函数式编程概念 1.2函数是一等公民 1.3高阶函数 1.5柯里化(Haskell Brooks Curry) 1.6函数组合(compose) 1.7Lodash中的f

时光海的博客 3268

深入理解 JavaScript 高阶函数:解锁函数式编程的强大能力

在 JavaScript 中,高阶函数接受一个或多个函数作为参数。返回一个函数作为其结果。简单来说,高阶函数就是操作函数的函数。它们将函数的概念提升到了一个新的抽象层次,允许我们创建更通用、更灵活的代码。// 示例 1: 接受函数作为参数// 输出: 8// 输出: 2// 示例 2: 返回一个函数return function(name) { // 返回一个匿名函数${

weixin_42107409的博客 962

SolidCraft 免费开源 CAD:AI 对话建模 + 浏览器零安装,6 天 1297 星的参数化工具

6 天,1297 颗星。很多项目吭哧吭哧做一年都未必破千,SolidCraft 上线一周就冲进热门榜。它瞄准的是个老问题:专业 CAD 软件又贵又难学,而它免费、开源,连安装都能省。 ## 一句话:这是个什么软件? SolidCraft 是一个用 Rust 从零写成的**免费开源参数化 3D CAD** 工具:画草图、加约束、拉伸旋转成实体,所有操作都记录在一条可编辑的"特征时间线"上。Win

gitblog_00436的博客 330
上一篇: 【基于JS 函数式编程 -1】什么是函数式编程 | 纯函数 | 命令式与声明式 | 优点
下一篇: 【基于 JS 函数式编程-3】柯里化 | 偏函数 | 组合与管道
蟹蟹蟹风流
蟹蟹蟹风流 优质创作者: 编程框架技术领域 优质创作者: 编程框架技术领域 领域专家: 前端开发技术领域 领域专家: 前端开发技术领域
博客等级 码龄11年 3万+粉丝 396原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蟹蟹蟹风流

期望和你分享一杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值