遞迴那點事

桔梗❤發表於2020-08-26

直接進入正題,什麼是遞迴?——簡單來說,遞迴就是函式內部呼叫自己。

幾個簡單的面試題,大家可嘗試一做,再看我寫的參考程式碼

1.求1-n的和。(如求1到100的和)

2.階乘

3.陣列扁平化,去重,並排序。( [2,[1,2],[0,9,5],[15,[[4]]]] )

4.斐波那契數列

 

問題很簡單,多想想多做做就出來了。

1.求1-n的和。(如求1到100的和)

function sum (num){
    if (num===1) {
        return 1
    } else {
        return sum(num-1) + num
    }
}
console.log(sum(100))   //5050

2.階乘

function factorial(x){
    if (x===1) {
        return 1
    } else {
        return factorial(x-1) * x
    }
}
console.log('factorial',factorial(3))

 3.陣列扁平化,去重,並排序

const arr = [2,[1,2],[0,9,5],[15,[[4]]]]
let result = []
function flat (arr){
    arr.forEach(item => {
        if (Array.isArray(item)) {
            flat(item)
        } else {
            result.push(item)
        }
    })
}
flat(arr)
result = [...new Set(result.sort((x,y) => x-y))]
console.log('遞迴',result)  //遞迴 (7) [0, 1, 2, 4, 5, 9, 15 

上面的方法是用遞迴的方法實現的,補充一提,可以用陣列的方法更簡單,如下

const flatArr = [...new Set(arr.flat(3).sort((x,y) => x-y))]
console.log('陣列方法flat',flatArr);    //陣列方法flat (7) [0, 1, 2, 4, 5, 9, 15]

4.斐波那契數列

function fibonacci (num){
    if (num===0) {
        return 0
    }else if (num===1) {
        return 1
    }else {
        return fibonacci(num-1) + fibonacci(num-2)
        // return arguments.callee(num-1) + arguments.callee(num-2)
    }
}
  console.log(fibonacci(6));  //8

 

有細心的朋友,可能會看到我上面的一段註釋,用到了arguments.callee,這就是要引出一個遞迴的一個相關問題了。

當我們拷貝了這個fibonacci函式後,又將該fibonacci函式重新賦值,會報錯 fibonacci is not a function,遞迴呼叫失敗,如下程式碼

const copyFibonacci = fibonacci
fibonacci = null
console.log('copyFibonacci',copyFibonacci(6));

原因也很簡單,報錯資訊也很明確指出來了,fibonacci不是一個函式,因為已經被我們設為null了。

要解決這一問題,有兩種方法

1. 使用arguments.calllee替代fibonacci這個名字進行遞迴呼叫,如上註釋 

return arguments.callee(num-1) + arguments.callee(num-2)

   arguments指的是實參列表,但是它有一個屬性callee,代表的是所處的函式本身

2. 使用命名函式表示式

let fibonacci = function f (num){
    if (num===0) {
        return 0
    }else if (num===1) {
        return 1
    }else {
        return f(num-1) + f(num-2)
    }
}

這樣無論fibonacci怎麼被賦值改變,函式f並沒有改變,這樣就不會報錯了。

雖然兩種方法都能解決這一問題,但是目前我們推薦使用的是第二種方法,因為方法一中的arguments.callee在嚴格模式中不允許使用。

同時其存在一些缺點:

  1.arguments是一個很大的物件,每次呼叫都要重新建立,影響瀏覽器效能
  2.遞迴中的this會不一樣,如下程式碼。(參考自:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments/callee)
var global = this;
var sillyFunction = function(recursed) {
    if (!recursed) { return arguments.callee(true); }
    if (this !== global) {
        // 會輸出 This is: [object Arguments]
        console.log('This is: ' + this);
    } else {
        console.log('This is the global');
    }
}
sillyFunction();

 

相關文章