Skip to content

递归

递归,也就是函数体中调用函数本身。递归一定符合下列的三个特点

  • 一定是因为存在某种“循环”逻辑才需要递归;
  • 一定存在某个终止条件(假设这个条件为 a),当符合这个条件时要结束(跳出)循环,类似 break 关键字;
  • 当 else 条件(上述所述终止条件的对立面),即“!a”条件时,需要调用函数本身,并返回调用函数本身的返回结果

例子一:

js
/**
 * 除去字符串中某种字符
 * @param word 字符串
 * @param char 要除去的字符
 * @returns {*} 除去某种字符后的字符串
 */
function deleteChar(word, char) {
  const index = word.indexOf(char)
  if (index < 0) { // 当字符串word中不含字符char时,无需再进行除去操作,退出循环
    return word
  }
  else { // 否则就要除去左起第一次出现的字符char,并递归调用函数本身
    const wordArr = word.split('')
    wordArr.splice(index, 1)
    const newWord = wordArr.join('')
    return deleteChar(newWord, char) // 返回整个函数
  }
}
console.log(deleteChar('chiaia', 'i'))

这里只是用于演示,除去字符串中某种字符最快的方式是使用字符串的replace方法,如'chiaia'.replace(/i/g, '')

例子二:

js
/**
 * 返回某个数的阶乘
 * @param a
 * @returns {number}
 */
function f(a) {
  if (a === 1) { // 当a的值为1时,它的阶乘就为1,退出循环
    return 1
  }
  else {
    return a * f(a - 1) // 返回整个函数
  }
}
console.log(f(5))

例子三:

返回某个数的所有公约数(以数组的形式)

js
const res = []
let n = 1
function f(a) {
  if (n > a) {
    return res
  }
  else {
    if (a % n === 0) {
      res.push(n)
    }
    n++
    return f(a)
  }
}
f(4)
console.log(res)

或者

js
/**
 * 返回某个数的所有公约数(以数组的形式)
 * @param a 某个数
 * @param n 起始于1的中间变量,循环递增,用于测试是否是某个数的公约数
 * @param res 用于存放公约数的数组
 * @returns {*[]}
 */
function f(a, n = 1, res = []) {
  if (n > a) { // n不可能大于a,否则说明我们已经尝试测试完了[1, a]之间的所有整数,无需再测试,退出循环
    return res
  }
  else {
    if (a % n === 0) {
      res.push(n)
    }
    n++
    return f(a, n, res) // 返回整个函数
  }
}
console.log(f(4))

后者实现方式避免了污染全局命名空间,明显优于前者