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

高阶函数

时间:2023-04-03 21:02:20 Node.js

NodeJS系列文章,本文为第一篇,首先预计后续会频繁使用逻辑字符串,其次是高阶函数、promises和事件机制。这篇文章主要是关于高阶函数的。call、bind、applycall和apply都改变了this的方向。不同的是接受参数的方式不同。call一个一个传递参数,apply接收一个数组。bind将创建一个称为绑定函数的新函数。当调用这个绑定函数时,绑定函数会在创建时传入bind方的第一个参数作为this,传入bind的第二个及以后的参数。参数加上运行时绑定函数本身的参数作为原函数的参数,以便调用原函数。当你想执行回调而不是在更改上下文后立即执行时,使用bind()方法,bind是返回的函数。apply,call会立即执行函数。这个点这个点的问题,谁来电,我就点给谁。在全局上下文的非严格模式和严格模式下,this指向顶级对象(浏览器中的window)。console.log(这个===窗口);//true'usestrict'console.log(this===window);//truethis.name='Hiraku';console.log(this.name);//Hiraku函数上下文普通函数调用方式varname='Xiaoma';varfunc=function(){console.log(this===window,this.name);//真正的小马(浏览器)};功能();在ES5中,全局变量挂载在顶层对象中(浏览器为window)。constname1='马红琴';constfunc1=function(){console.log(this===window,this.name1);//真正的未定义};constfunc2=()=>{console.log(this===window,this.name1);//真正的未定义};func1();func2();let不给顶层对象(浏览器是window)添加属性,window.name1和window.func1、window.func2都是undefined。在严格模式下,普通函数中的this表现不同,如未定义。'使用严格';varname2='小马';varfunc3=function(){console.log(this===window);//错误的};func3();call,apply的一个作用就是把this指向的函数改成第一个参数。如果第一个参数是undefined或者null,在非严格模式下,它指向window。在严格模式下,它指向第一个参数。对象中的函数(方法)调用模式varname3='Hiraku_Ma';varfunc4=function(){console.log(this,this.name);}varobj={name:'mhq',func:func4,attr:{name:'mhq-gs',func:func4,}}obj.func();//对象{}obj'mhq'obj.attr.func();//Object{}obj.attr'mhq-gs'//使用调用类比:obj.func.call(obj);//'mhq'//使用调用类比:obj.attr.func.call(obj.attr);//'mhq-gs'函数赋值后变成普通函数。构造函数调用方式functionExample(type){this.type=type;控制台日志(这个);//返回这个;}varexp=newExample('mhq');使用new运算符调用函数,它会自动执行以下步骤:创建一个全新的对象。该对象将实现[[Prototype]](即__proto__)链接。生成的新对象绑定到函数调用的this。通过new创建的每个对象最终都将[[Prototype]]链接到函数的原型对象。如果函数没有返回对象类型Object(包括Functoin、Array、Date、RegExg、Error),那么new表达式中的函数调用会自动返回这个新对象。当调用new运算符时,this指向生成的新对象。调用new时的返回值,如果没有显式返回对象或函数,则为新生成的对象。函数示例(类型){this.type=type;控制台日志(这个);返回{};}varexp=newExample('mhq');//{名称:mhq}console.log(exp);//如果返回函数f,则exp为函数f,如果为对象{},则exp为对象原型链中的调用模式{}functionExample(type){this.type=type;控制台日志(这个);}Example.prototype.func=function(){console.log(this.type);};varexp=newExample('mhq');exp.func();箭头函数调用方式varname='mhq';varstudent={name:'hiraku',func:function(){vararrowFunc=()=>{console.log(this.name);}arrowFunc();},arrowFunc2:()=>{console.log(this.name);}}student.func();//'hiraku'student.arrowFunc2();//'mhq'语法更简洁明了。箭头函数不会创建自己的this。箭头函数的this指向定义的地方外层第一个普通函数与使用位置无关。继承的普通函数的this点发生变化,箭头函数的this点也会随之变化。箭头函数外层没有普通函数,它的this在严格模式和非严格模式下都是指向window(全局对象)的call()、apply()、bind()不能改变this的指向在箭头函数中Arrowfunctioncannotbeusedasconstructorundeclarederror当箭头函数的this指向普通函数时,其参数继承普通函数rest参数(...extension)取参数箭头函数没有原型prototype箭头函数不能作为Generator函数,不能使用yeild关键字varobj={name:'mhq',func:function(){console.log(this.name);return()=>{console.log('arrowFn:',this.name);}}}varobj1={name:'hiraku',}obj.func().call(obj1);//'mhq''arrowFn:''mhq'obj.func.call(obj1)();//'hiraku''arrowFn:''hiraku'DOM事件处理程序调用onclick和addEventerListener指向绑定到事件的元素。有些浏览器,比如IE6~IE8下的attachEvent,this指向window。高阶函数(Higher-orderfunction)什么是高阶函数?高阶函数至少满足两个条件:它接受一个或多个函数作为输入,并输出一个函数。也可以理解为函数的参数是一个函数(回调函数);一个函数返回一个函数(函数柯里化)。我们在写代码的时候不想破坏原来的逻辑,所以我们把原来的逻辑用一个函数包装起来,里面实现了自己的逻辑,也就是切片编程。constoriginFunc=(...args)=>{console.log('原始函数',args);};//希望在调用originFunc之前做一些事情,使用Function.prototype扩展一些函数到每个函数Function。prototype.before=function(cb){return(...args)=>{cb();这(...参数);};};让newFunc=originFunc.before(()=>{console.log('beforeoriginFunc');});newFunc('a','b','c');一个异步并发问题我同时发送多个请求,希望得到最终结果functionafter(time,fn){return()=>{if(--time===0){fn();}};}constexaFun=after(3,()=>{console.log('Execute');});exaFun();exaFun();exaFun();示例:constfs=require('fs');constpath=require('路径');functionrun(times,fn){constrenderObj={};return(key,value)=>{renderObj[key]=value;控制台日志(键,值);if(--times===0)fn(renderObj);};}letout=run(2,(result)=>{console.log(result);});fs.readFile(path.resolve(__dirname,'1.txt'),'utf8',(_,data)=>{console.log(data,'ddd')out('1',data);});fs.readFile(path.resolve(__dirname,'2.txt'),'utf8',(_,data)=>{out('2',data);});FunctioncurryingCurrying,即柯里化,为了给多参数函数提供一种递归退化的方法,将提供多个参数的函数转化为接受单个参数并返回剩余参数的函数和一个返回参数的新函数结果constcurring=(fn,arr=[])=>{//length为参数个数letlen=fn.length;return(...arg)=>{arr=[...arr,...arg];如果(arr.length{返回a+b+c+d+e+f+g+h;}console.log(curring1(add,[1,2])(3,4)(5)(6)(7,8))//15个函数curry使用场景:参数重用、延迟执行、预加载、动态创建功能、发布和订阅以及观察者模式。观察者模式定义了对象之间一对多的依赖关系。当一个对象的状态发生变化时,所有依赖它的对象都会收到通知并自动更新。它属于行为模式。行为模式侧重于对象之间的通信。观察者与被观察者之间的通信constfs=require('fs');conste={arr:[],on(fn){this.arr.push(fn);},emit(){this.arr.forEach(fn=>fn());}};e.on(()=>{console.log('读取数据');})constrenderObj={};e.on(()=>{if(Object.keys(renderObj).length===2){console.log('读取完成');}});fs.readFile('./name.txt','utf8',(_,data)=>{renderObj['name']=data;e.emit();});fs.readFile('./age.txt','你tf8',(_,data)=>{renderObj['age']=数据;e.emit();});发布和订阅是提前存储的,后面发布的时候,让预订阅的东西执行,发布者和订阅者是不直接通知的,也就是说发布者和订阅者是不认识的,发布者和订阅者之间有第三个组件,称为调度中心或事件通道,它维护发布者和订阅者之间的连接,过滤所有从发布者传入的消息并相应地分发给订阅者classSubject{//Observerconstructor(name){this.name=姓名;这个.arr=[];这。state='我很开心';}attach(observer){//根据发布订阅注册观察者this.arr.push(observer);}setState(newState){this.state=newState;this.arr.forEach(o=>o.update(this));//通知所有观察者我的状态已经改变}}classObserver{//Observerconstructor(name){this.name=name;}更新{控制台。log(s.name+'当前状态是'+s.state+'右:'+this.name);}}lets=newSubject('baby');leto1=newObserver('我');leto2=newObserver('我的妻子');s.attach(o1);s.attach(o2);控制台日志(s.state);s.setState('不开心');本文涉及的高阶函数包括函数柯里化、发布-订阅|观察者模式等,下一篇将分享Promise的源码,手写Promise。