ES6被你忽略的尾調用


title: 被你忽略的‘尾調用’
date: 2017-05-02 16:52:22javascript

tags: [ES6,javascript]

尾調用是什麼?

在ES6有一個新特性:尾調用
用最簡單的一句話描述就是‘某個函數的最後一步再調用另外一個函數’,聽起來挺簡單的,
可是它的功能特別強大,直接給你擼個例子吧。java

function a(x) {
  return b(x);
}

這個函數的最後一步再調用另外一個函數,這就是尾調用。
如下幾點不屬於尾調用函數

  1. 在調用函數b以後還存在賦值的操做優化

function a(x) {
    let m = b(x);
    return m;
}
  1. 返回的那個函數沒有加returncode

function a(x) {
    b(x);
}
  1. 在調用以後還存在其餘的賦值操做遞歸

function a(x) {
    return b(x) - 2;
}

尾調用優化

函數調用會在內存造成一個"調用記錄",又稱"調用幀"(call frame),保存調用位置
和內部變量等信息。若是在函數A的內部調用函數B,那麼在A的調用記錄上方,還會造成
一個B的調用記錄。等到B運行結束,將結果返回到A,B的調用記錄纔會消失。若是函數B
內部還調用函數C,那就還有一個C的調用記錄棧,以此類推。全部的調用記錄,就造成一個
"調用棧" ip

來個例子吧內存

function a() {
  let p = 2;
  let q = 3;
  return b(p + q)
}
a();

仔細觀察上面的代碼就會發現啊a函數彷佛是多餘的吧,由於b函數是尾調用函數,執行到
這裏函數a早就結束了,徹底能夠刪除a函數了只保留b的調用幀便可。
尾調用優化:就是隻保留內層函數的調用幀,若是全部函數都是尾調用,那麼徹底能夠
作到每次執行時調用幀只能有一項,將大大節省內存,也就是尾調用的意義所在了。
注意的一點就是內層函數運用了外層函數的變量便不能進行尾調用優化了。記住哦!it

尾遞歸

顧名思義,在一個尾調用中,若是函數最後的尾調用位置上是這個函數自己,則被稱爲尾遞
歸。遞歸很經常使用,但若是沒寫好的話也會很是消耗內存,致使爆棧。可是對於尾遞歸來講只
存在一個調用棧,便永遠不會發生「棧溢出」錯誤。
就以求一個給出數的階乘來探索吧。在傳統的作法中利用n的遞減乘上原函數,這樣複雜度
便會很高,數據量一大便會發生「棧溢出」的錯誤了。 io

傳統的解決辦法

function f(n) {
  if(n === 1) {
    return 1;
  }
  return n * f(n -1);
}

尾調用

function a(n, t) {
  if(n === 1) {
    return t;
  }
  return a(n - 1, n * t)
}

對比兩個解決辦法的複雜度就知道,尾調用只保留了一個記錄。

相關文章
相關標籤/搜索