当前位置: 首页 > Web前端 > HTML

es6系列-尾调用

时间:2023-03-28 18:23:57 HTML

1.尾调用1.在函数末尾调用其他函数的概念是尾调用functiona(x){returnb(x)}2.区分两种类型尾调用//这种情况不是尾调用//return语句不能包含表达式functiona(x){returnb(x)+1}functiona(x){lety=b(x)returny}functiona(x){b(x)//returnundefined}//尾调用不一定在函数的最后,主要是函数的最后一步可以完成functiona(x){if(x>1){returnm(x)}returnn(x)}3.尾调用优化函数调用都会有一个“调用记录”,也称为“调用帧”,用于保存调用位置、内部变量等信息.如果在函数A中调用了函数B,那么B的调用记录会记录在A的调用记录中,B执行完后将结果给A,B就消失了。如果函数B调用函数C,也会产生一条C的调用记录,依次类推,所有记录形成一个“调用栈”。例如functionA(){console.log(11)}functionB(){console.log(22)A()}functionC(){console.log(33)B()}C();//执行顺序A()->B()->C()332211使用尾调用优化实现functionA(){console.log(11111)}functionB(){console.log(22)returnA()}functionC(){console.log(33)returnB()}C();//当执行顺序A()->B()->C()执行函数C时,最后一步返回函数B、因此可以删除函数C,所以调用栈中只有一个函数B。函数B执行后,函数A被调用,函数B的记录也被清除。以此类推,可以得出结论,因为尾调用是函数的最后一步,是不需要记录日志的,所以调用栈中一直保留着一个调用帧。函数f(){让m=1;让n=2;returng(m+n);}f();//等价于functionf(){returng(3);}f();//等价于g(3);上面代码中,如果函数g不是尾调用,函数f需要保存内部变量m和n的值,g的调用位置等信息。但是在调用g之后,函数f就结束了,所以f()的调用记录在执行的最后一步可以完全删除,只保留g(3)的调用记录。只有当外层函数的内部变量不再使用时,内层函数的调用框架才会替换外层函数的调用框架,否则无法进行“尾调用优化”。这叫做“尾调用优化”(Tailcalloptimization),即只保留内部函数的调用记录。如果所有的函数都是尾调用,完全可以每次执行只有一条调用记录,这样会大大节省内存。这就是“尾调用优化”的意思。注意目前只有Safari浏览器支持尾调用优化,Chrome和Firefox不支持2.尾递归1.普通递归概念:函数自身内部调用解决的问题:①数据的定义按照递归(阶乘)②问题通过递归(回溯)实现求解③数据结构通过递归定义(树结构,二叉树)缺点:运行效率低,容易造成栈溢出2.尾递归概念:尾调用自身称为尾递归。递归很耗内存,每次调用都要保存所有的调用记录,容易出现栈溢出错误。但是对于尾调用,每次只有一条调用记录,不会出现栈溢出,节省内存,优化性能。(1)实现阶乘递归实现:functionfactorial(n){if(n===1)return1;returnn*factorial(n-1);}factorial(5)//120调用形式:factorial(5)5*factorial(4)5*(4*factorial(3))...//计算阶乘时,n个调用记录需要保存,复杂度为O(n)//执行多层时会出现栈溢出Tailcall递归实现:functionfactorial(n,total){if(n===1)returntotal;returnfactorial(n-1,n*total);}factorial(5,1)//120调用形式:factorial(5,1)factorial(4,5)factorial(3,20)...//尾递归保留记录,复杂度为O(1)。在执行阶乘函数factorial(5,1)时,当调用堆栈次数过多并且调用堆栈会增长直到达到限制:浏览器硬编码堆栈大小或内存不足。会出现“Maximumcallstacksizeexceeded”,斐波那契数列也能体现尾递归的重要性(2)斐波那契数列普通递归:functionfeibonaqi(n){if(n<=1)return1;returnfeibonaqi(n-1)+feibonaqi(n-2);}feibonaqi(10)//89feibonaqi(20)//报错尾递归:functionfeibonaqi(n,a1=1,a2=1){if(n<=1)返回a2;returnfeibonaqi(n-1,a2,a1+a2);}feibonaqi(10)//89feibonaqi(20)//109463、函数递归重写尾递归递归函数的实现需要重写,保证最后一步函数调用自身。使用的内部变量需要变成函数的参数。在上面的例子中,阶乘函数factorial使用了一个中间变量total,total在执行阶乘时作为函数的参数。一开始传入了5和1两个参数,第一次看可能不太直观。有两种方法可以解决。方法一:除了尾递归函数外,再提供一个函数functiontailFactorial(n,total){if(n===1)returntotal;returntailFactorial(n-1,n*total);}functionfactorial(n){returntailFactorial(n,1);}factorial(5)//120函数公式有一种编程方法——“函数柯里化”。之所以在多参数函数转单参数函数时需要优化尾递归,是因为调用栈太多,造成溢出,所以只要减少调用栈就不会溢出。方法二:使用ES6函数的默认值functiontailFactorial(n,total=1){if(n===1)returntotal;returntailFactorial(n-1,n*total);}factorial(5)//1203、函数柯里化函数柯里化是将一个多参数函数转化为单参数函数//在柯里化之前——普通加法函数functionsum(x,y){returnx+y;}sum(2,3)//5//柯里化函数sum(x){returnfunction(y){returnx+y;}}//箭头函数constadd=a=>b=>a+b;sun(2)(3)//constf=sum(1)//f(2)尾递归阶乘函数使用柯里化转换多个参数转化为一个单参数函数(n===1)返回总数;返回tailFactorial(n-1,n*total);}constfactorial=currying(tailFactorial,1);factorial(5)//1204.严格模式ES6尾调用优化只在严格模式下启用,普通模式无效。这是因为在普通模式下,函数内部有两个变量可以跟踪函数的调用堆栈。arguments:调用时返回函数的参数。func.caller:返回调用当前函数的函数。当发生尾调用优化时,函数的调用栈被重写,所以上面两个变量会被扭曲。严格模式禁用了这两个变量,所以尾调用模式只在严格模式下生效。