当前位置: 首页 > 后端技术 > Node.js

JavaScript基础强化:函数(高阶函数)的应用

时间:2023-04-03 15:39:34 Node.js

学习的东西总是需要记录的。学得越多,明年的薪水就会越高。来吧,老头。我可以!!!!高阶函数通俗意义上的所谓高阶函数是指满足以下两点中的任意一点就是高阶函数:函数的参数是函数functiona(){}a(()=>{})afunctionReturnafunctionfunctionb(){returnfunction(){}}根据我们的经验,我们可以知道第一个参数是一个函数,我们称之为回调方法。返回函数的第二种方法可以称为拆分方法(拆分出一个具有复杂功能的函数)。结合上面两个定义,我们写一个在方法前封装函数时常用的例子:我们希望在调用方法前先调用before函数,再调用核心函数函数(一次一个注解,大佬不多和我一样细心的兄弟们)//核心函数functionconstcore=()=>{console.log('World')//就这么一条打印出来的信息根本就不是核心好吗?自己处理吧,肚子有限}//我们要实现的功能是调用newCore先打印Hello再打印WorldconstnewCore=core.before(()=>{console.log('Hello')})//如果不是很满意,也可以调用newCore1constnewCore1=core.before(()=>{console.log('FuckThe')})//然后可以看到我们已经实现了核心functioncoreReuse又是如何实现的呢?实现上面功能的代码其实很简单//在原型上扩展一个方法Function.prototype.before=function(beforeFn){//参数是一个函数return()=>{//返回传入的参数通过一个函数调用然后调用corebeforeFn()this()//上面是核心调用的before,所以这里this才是核心}}接下来就可以看到效果了,全部如下://在原型上扩展一个方法Function.prototype.before=function(beforeFn){//参数是一个函数return()=>{//返回函数调用传入的参数,然后调用corebeforeFn();这();//箭头函数没有this指针,所以找外层,上面是核心调用的before,所以这里this才是核心};};//核心函数functionconstcore=()=>{console.log("世界");//就是这样打印消息根本不是核心,好吗?让我们处理一下看看。肚子里的油是有限的};//我们要实现的功能是调用newCore先打印Hello再打印WorldconstnewCore=core.before(()=>{console.log("Hello");});//如果不是很满意,也可以调用newCore1constnewCore1=core.before(()=>{console.log("FuckThe");});//这样可以看到我们实现了它的核心功能core的复用是如何实现的呢?继续往下看//接下来我们试试结果newCore();//你好世界newCore1();//FucktheWorld什么?你还想传参数吗?哎呀,你真是个聪明的小鬼。这是传递参数的版本。Function.prototype.before=function(beforeFn){return(...arg)=>{//箭头函数不仅没有this,而且没有arguments,所以我用算术运算符把所有的参数收集进去数组beforeFn();这(...参数);//然后我一个一个展开放到核心};};//核心函数functionconstcore=(...arg)=>{//然后我把参数集合称为数组然后打印出来console.log("世界",arg);};constnewCore=core.before(()=>{console.log("Hello");});constnewCore1=core.before(()=>{console.log("FuckThe");});//接下来我们试试结果。我想在这里传递多少?可以传递多少个参数?新核心(1,2,3,4,5,6);//HelloWorld[1,2,3,4,5,6]//也可以不传,打印一个空的ArraynewCore1();//FucktheWorld[]趁热发布订阅,再看一个例子,react的事务组列的原理可以在某物前后添加方法,也是发布订阅的一种应用。既然说到了发布和订阅,那我们就简单说两句吧(就两句话)。Feature1预先定义一个东西,当有事情发生的时候执行它(这个可以用白话做)。特点2发布和订阅之间没有关系。好了,两句我写完了(可以这样总结,流行了也不知道怎么写)。也可以用一些真实的例子来理解,比如:我想在9点吃一个苹果预先定义了一个东西:吃一个苹果(也就是订阅)等待某事发生后再执行:9o'clock(也就是release)和吃apple没有关系(满足特征2)掩饰理解一点好吗???(大概吧?)说到代码,下面的例子是发布和订阅的简单应用constperform=(anymethod,wrappers)=>{//wrappers是下面传入的数组wrappers.forEach(wrap=>{wrap.初始化();});任何方法();wrappers.forEach(wrap=>{wrap.close();});};perform(()=>{console.log("核心功能");},[{initilizae(){//(订阅)控制台.log("Start1");},close(){console.log("End1");}},{initilizae(){//(订阅)console.log("Start2");},close(){console.log("End2");}}]);//输出应该是1开头,2开头和结尾核心函数时间1结束时间2然后我们看再举个例子,使用publish和subscribe来处理并发问题,不过在此之前,我们先来看一下counter方法处理并发问题的例子,来了解这个故事的前因后果(想跟我讲故事吗?)//请在node环境中执行这段代码或者在vscode中安装插件coderunner,在你当前项目的根目录下新建两个文件,name.txtage.txtconstfs=require('fs')让信息={}让索引=0函数out(){if(index===2){//当为2时,info对象中有name和age属性,无论这两个文件先读取哪个,都不会影响输出结果console.log('这种方式有点low',info)}}fs.readFile("name.txt",'utf8',(err,data)=>{info['name']=dataindex++out()})fs.readFile("age.txt","utf8",(err,data)=>{info["age"]=dataindex++out()});再来看优雅的实现,使用高阶函数,可以看出高阶函数在实际应用中还是很广泛的//先写一个after函数constafter=(times,fn)=>()=>--times===0&&fn()//当参数times减少时返回一个方法为0时,执行回调方法letinfo={}constout=after(2,()=>console.log(“优雅”,信息));fs.readFile("name.txt","utf8",(err,data)=>{info["name"]=data;out();});fs.readFile("age.txt","utf8",(err,data)=>{info["age"]=data;out();});现在故事情节差不多明白了,我们来看看发布和订阅的实现//implementbypublishandsubscribe//useonsubscribe,emittopublishandimplementlete={arr:[],on(fn){this.arr.push(fn);},emit(){this.arr.forEach(fn=>fn());}};letinfo={};e.on(()=>{console.log("ok");});//想写多少就写多少e.on(()=>{//订阅如果(对象.keys(info).length===1){console.log("该订阅将在长度为1时发布");}});e.on(()=>{//subscribeif(Object.keys(info).length===2){console.log("PublishSubscribeImplementation",infoOnEmit);}});fs.readFile("name.txt","utf8",(err,data)=>{info["name"]=data;e.emit();//emit});fs.readFile("age.txt","utf8",(err,data)=>{info["age"]=data;e.emit();//publish});以上就是标准的publish和subscribe的写法,应该够应对面试题了on-subscribeemit-publish,台下的同学跳起来说:“怎么样,这跟eventin的太像了Vue》,这位同学请坐,你说的对,Vue中有很多发布订阅应用,不过别着急,我还没写,后面会在本博客源码中陆续分析.(标号为源代码)函数柯里化函数柯里化是高阶函数,但是什么时候是函数柯里化,通俗点说就是把一个大函数拆分成多个函数(大概就是不停地返回函数)真相还是需要练习,所以还是以看例子为主,然后继续理解剧情。现在我们要写一个类型判断的方法。如何实现通用类型判断?Object.prototype.toString.call()//试试两个console.log(Object.prototype.toString.call("123"));//[对象字符串]console.log(Object.prototype.toString.call([123]));//[objectArray]然后看看通用封装是如何实现的constcheckType=(content,type)=>{returnObject.prototype.toString.call(content)===`[object${type}]`;};constb=checkType(123,"Number");console.log(b);//真正的函数没有问题,那么故事就结束了。为什么!!导演导演,剧本不是这样的!!嗯,我想我可以回家吃火锅了!确实,上面的通用包实现了判断类型的功能。每次判断的时候都是手动写的所有类型。老人家太难了,我也太难了(尺神经受伤已经好几个月没法玩了,还要在前线战场上挣扎(排)斗(水)搬砖).咳嗽!谈论猪蹄!呸!是主题。恩主题!所以我们尽量不要每次判断都自己写类型,这样我们就该柯里化应用函数了。让我们先试试基本版。//Curry实现(简单的基本版本)constcheckType=type=>{returncontent=>{returnObject.prototype.toString.call(content)===`[object${type}]`}}constisString=checkType('String')//returntheinnerfunctionconsole.log(isString("123"))//true到目前为止,我已经成功地应用了函数柯里化,将一个方法拆分为多个方法(这篇文章够长,我只是写aString)并且每个我只写一个类型,这减少了出错的机会。“这也太麻烦了,要写那么多类型,好烦,我懒。”好吧,我的同学,你是对的。这个世界出了什么问题,懒惰是我们的第一动力。所以上面是基本版本,只是添加如下constcheckType=type=>{returncontent=>{returnObject.prototype.toString.call(content)===`[object${type}]`}}constutils={}//声明一个实用方法对象constTYPES=["Number","String","Object","Array","Boolean"]//没事没事,伤了TYPES。forEach(type=>{utils[`is${type}`]=checkType(type)})也很简单吧?哎呀!代码太有趣了,我的天哪。观察者模式观察者模式的特点有三个。观察者和被观察者是相连的。观察者存储在观察者中。观察者模式包括发布和订阅。继续阅读代码。我已经敲了几千遍代码,代码对我来说已经很不错了。第一次~当然不会出现这种事,正所谓代码敲一百次,意思就出来了。敲多了没关系,肯定好处多多//ObserverclassSubject{constructor(){this.arr=[];this.state="我不饿";}//通过此方法将观察者存储在arr中attach(o){this.arr.push(o);}setState(newState){//使用此方法设置状态并通知观察者this.state=newState;this.arr.forEach(o=>o.update(newState));}}//观察者类Observer{constructor(name){this.name=name;}update(newState){//使用此方法更新观察到的状态console.log(`${this.name}知九儿${newState}`);}}leto1=newObserver("Mopecat");//创建一个观察者Mopecat(me)leto2=newObserver("Sean");//创建一个观察者Sean(我老婆)lets=newSubject("Jiuer");//九儿是我的猫s.attach(o1);//将o1存储在观察对象中,即Mopecats.attach(o2);//将o2存储在观察者中,即Seans.setState("hungryagain");//更新状态//将输出Mopecat知道九个孩子又饿了//Sean知道九个孩子又饿了。上面也是观察者模式的一个简单例子。将观察者的信息存储在observed中,直到状态更新后再通知观察者的过程就是发布和订阅的应用,被观察者和观察者是Connected的:观察者存储在observed中。好像上面三个特征可以改成两个。算了,总结一下吧,高阶函数的应用在我们的工作中可以说是无处不在。学好它们之后,我们可以写出更优雅的代码。让我们一起加油吧。同时,来收藏我的博客,互相添加友情链接吧!我的朋友!!