严于律己,宽以待人。
真的想不起来以前是怎么学习的,现在是学什么忘什么,好沮丧。
递归和回调明明都是以前学过的,可是书到用时方恨少啊。
递归
别说曾经,我现在也还迷递归。我只知道自己调用自己,再给个结束条件就算完了。可是当我自己着手写递归的时候,就会遇到很多很多问题。
我曾在很多地方找过递归的解释
编程语言中,函数Func(Type a,……)直接或间接调用函数本身,则该函数称为递归函数。递归函数不能定义为内联函数。
数学定义: 对于某一函数f(x),其定义域是集合A,那么若对于A集合中的某一个值X0,其函数值f(x0)由f(f(x0))决定,那么就称f(x)为递归函数。
递归的执行形状看起来就像这样,从 1 到 5,再从 5 到 1.
1 | 1 |
或许实在上高中的时候,我们就该学过斐波那契数列
,时隔四年,也实在是想不起来,只记得定义是这样的:设有 f(x) 在取值范围内从第三项开始,每一项都是前两项的和。用数学表达式可以这样写 f(x) = f(x-1) + f(x-2)
。
斐波那契可以简单表示为:[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, …]
这样的问题,通常使用递归就很简单了,但是,不会递归的我们只能用循环了。
1 | function fb(num) { |
这个函数写出来的瞬间,我怎么觉得我凉了呢???这个函数其实很简单,不过就是把第三项之后的值通过计算存入数组。但是我们需要的只是数组最后一个值,所以这样就显得累赘。
后来,学了递归之后,就可以开始使用递归写了,就像一开始所说,调用当前函数不断执行下去,直到停止条件后开始一层一层返回数据。
1 | function fb(n) { |
回调
回调跟递归有一个类似的地方,就是在函数内执行一个函数。但是递归调用的是自己,而回调是调用其他函数。
1 | function fun(cb) { |
其中回调函数也可以用匿名函数,箭头函数表示。
我们常见的许多js原生函数都是需要回调函数来执行的比如:
1 | let arr = [1,2,3,4,5,6,7,8,9] |
关于尾递归和尾调用优化
“尾”这个字突出了它的位置。关于这个函数的位置就是在函数的最尾部。
1 | function factorial(n) { |
尾调用或者尾递归都必须是在最尾部,如上面例子不算是尾递归,因为在 return 之后还会执行 n * factorial(n - 1)
。
尾调用由于是函数的最后一步操作,所以不需要保留外层函数的调用帧,因为调用位置,内部变量等信息不会再使用了,直接用内函数的调用帧取代外层函数即可
尾递归的优化,每次执行时栈内只有一个帧,将大大的节省内存。