太长不看版:
一个函数,如果直接或者间接调用自己,那么它就是递归函数。其中,斐波那契数列就是一个用递归解决问题的常见例子,但其实递归有着更多的应用比如文件系统的目录结构,嵌套数据结构的校验和转换等
什么是递归?
我们顺便来学个单词: recursion
。它是递归的意思,有点不直观,我们来看看另一个 curve
曲线,那么 re-curve
就是不断重复的曲线了。
The factorial of a positive integer n, denoted by n!, is the product of all positive integers less than or equal to n.
整数n的阶乘,记做 n!,是所有小于或者等于 n 的整数的结果。比如,要计算 5 的阶乘:
5! = 1 * 2 * 3 * 4 * 5
= 2 * 3 * 4 * 5
= 6 * 4 * 5
= 24 * 5
如果我们想计算 6 的阶乘,只需要把上面的结果再乘以 6。7 的阶乘就是 7 * 6!如果我们把结果整理成一个表,会发现如下模式:
n | !n | - | - |
---|---|---|---|
1 | 1 | - | - |
2 | 2 * 1 | = 2(1!) | = 2 |
3 | 3 * 2 * 1 | = 3(2!) | = 6 |
4 | 4 * 3 * 2 * 1 | = 4(3!) | = 24 |
5 | 5 * 4 * 3 * 2 * 1 | = 5(4!) | = 120 |
可以看出在表格中的每一行都依赖之前的内容,除了第一行。第一行一般叫做递归的基类。这就意味着如果我们想要计算任何正整数 n 的阶乘,我们从计算 n - 1 开始,然后会计算 n - 2,直到到达基类 !1 = 1 为止。
代码
下面是递归函数的 Javascript 实现:
const factorial = n => {
if (n <= 1) {
return 1
}
return factorial(n - 1) * n
}
用三目来简写如下:
const factorial = n => n <= 1 ? 1 : factorial(n - 1) * n;
如果忘记了基类条件呢?
如果我们移走之前代码中的条件判断,只保留递归调用的话:
const factorial = n => factorial(n -1) * n
这样的调用会引发堆栈溢出,俗称爆栈,也叫内存泄漏。大多数时候,没有基类的递归调用不应该存在。但是在尾递归中有所不同。
递归很酷,但是能做点什么有用的吗?
终于要回归正题了。我们假设在带宽有限的情况下,数据传输很慢。所以需要优化数据接口,我们需要把一个对象中的任意嵌套深度的 null 值给过滤掉,这个时候,递归就派上用场了,直接上代码。
注释即正文:
const isObject = value => Object(value) === value
const removeNullValues = object =>
/* `Object.entries()`方法返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环也枚举原型链中的属性)。
以上来自MDN的介绍,通过 Object.entries 我们可以得到一个 [key, value] 排列的数组,方便接下来的 reduce 直接使用。
*/
Object.entries(object).reduce((acc, [key, value]) => {
// 当值为 null 时,过滤掉
if (value === null) {
return acc
}
if (isObject(value)) {
// 如果value 是一个引用类型,也就是说 value 里面还有对象,我们就需要再次调用函数自身进一步解析里面的值,过滤 null
const filteredValue = removeNullValues(value)
// 也有可能这个对象是一个空对象,也是我们不想要的,剔除空对象操作,比如: dolor: {}
if (Object.entries(filteredValue).length === 0) {
return acc;
}
// 最后函数从这里结束并返回,函数removeNullValues 会得到正确的结果
return { ...acc, [key]: filteredValue }
}
// 这一步操作是跟reduce用法相关,我们需要收集每次遍历后获取的有效属性, acc 是一个累计值
return { ...acc, [key]: value }
}, {})
removeNullvalues({
foo: 'foo',
bar: null,
baz: {
qux: null,
quux: 'quux',
corge: {
lorem: null,
ipsum: null,
dolor: {}
}
}
})
// [object Object] {
// baz: [object Object] {
// quux: "quux"
// },
// foo: "foo"
// }
本文完,感谢阅读!
