Javascript調用棧與尾遞歸實現(xiàn)

JavaScript函數(shù)調用棧

理解JavaScript的函數(shù)調用棧是非常重要的募舟。它可以讓我們知道Javscript在函數(shù)調用時是怎么運行的薄啥。

1. 函數(shù)調用棧(call stack)

調用棧是一種棧結構,它用來存儲計算機程序執(zhí)行時候其活躍子程序的信息梆掸。(比如什么函數(shù)正在執(zhí)行解寝,什么函數(shù)正在被這個函數(shù)調用等等信息)驼仪。

下面我們以一個簡單的例子來看看什么是調用棧已脓。

function test1(){
    var a = 1;

    return function test2() {
            return a + 1;
    }
}
console.log(test1()());

對代碼分析如下:

  • 首先運行consol.log(). 這個時候log()方法被調用.入棧,形成一個棧幀珊楼。
  • 然后調用 test1。test1 被調用度液,入棧厕宗。又形成一個新的棧幀画舌,這個棧幀在之前的棧之上。
  • tes1 返回值是一個函數(shù)test2已慢,這個時候被調用曲聂,同時也生成一個棧幀。
  • 當test2運行完之后蛇受,返回a+1句葵;被推出棧,test1與console.log()依次也被推出棧幀兢仰。

還有另外一種情況乍丈。在JS中,我們知道還要處理很多異步操作把将,比如http請求轻专、setTimeout等。他們之中包含有異步函數(shù)察蹲。對于這種異步函數(shù)的調用请垛,JS會把他們放到運行隊列中,在運行之前他們首先會看調用棧里面是否有等待執(zhí)行的函數(shù)洽议,異步函數(shù)只會在等待調用棧被清空之后才會入棧執(zhí)行宗收。

對于setTimeout, 它是在等待某個時間之后進入運行隊列。

小結:

  1. 當函數(shù)被調用時亚兄,會入棧混稽,形成棧幀。
  2. 當函數(shù)里在調用函數(shù)時审胚,內部函數(shù)會在外部函數(shù)上再次形成棧幀匈勋。
  3. 函數(shù)運行完之后,會被推出棧膳叨。
  4. 異步函數(shù)運行洽洁,首先進入運行隊列,然后在進入棧菲嘴。
2. Javascript遞歸
  1. 遞歸函數(shù)時在一個函數(shù)通過名字調用自身的情況下構成的饿自。如:
    function factorial(num){
        if(num === 1) return num;
        return num * factorial(num -1);
    }

但是上面的例子在我處理一個非常大的數(shù)的階乘的時候,就會發(fā)生如下的事情:

 function factorial(100000) //Uncaught RangeError: Maximum call stack size exceeded

對龄坪,沒錯昭雌。造成了棧溢出。那我們怎么辦呢悉默?

  1. 尾遞歸
    對于尾調用可以參考一下阮一峰老師對尾調用以及尾遞歸的實現(xiàn)的講解城豁。
    我們使用之前factorial的例子苟穆,來進行尾遞歸抄课。代碼如下:
function factorial(num,total){
    if(num === 1) return num;
    return factorial(num -1 ,num*total);
}
factorial(10,1) // 3628800

使用尾遞歸可以很輕松的解決了遞歸棧溢出的問題(只保留了內層函數(shù)的調用幀)唱星。

記住:只有不再用到外層函數(shù)的內部變量,內層函數(shù)的調用幀才會取代外層函數(shù)的調用幀跟磨,否則就無法進行“尾調用優(yōu)化”间聊。

但是上面的尾遞歸只能在Javascript的嚴格模式下才能有用。

  1. 自己實現(xiàn)尾遞歸

怎么實現(xiàn)尾遞歸呢? 我們可以用'循環(huán)函數(shù)'代替遞歸函數(shù).

(1). 使用 trampoline函數(shù)實現(xiàn)尾遞歸抵拘。

還是以之前階乘的例子為例哎榴。我們知道如下代碼會報出棧錯誤。

function factorial(num){
        if(num === 1) return num;
        return num * factorial(num -1);
}
    

我們實現(xiàn)一個 trampoline函數(shù)僵蛛,這個函數(shù)會不斷的循環(huán)另外一個函數(shù)尚蝌。

function trampoline(f){
 while (f && f instanceof  Function){ // 知道f 不是函數(shù) 或空為止,跳出循環(huán)充尉。
        f = f();
 }
    return f;
}

那么 trampoline函數(shù)里的參數(shù)是f是實現(xiàn)階乘邏輯的函數(shù)飘言,也是我們用'循環(huán)'代替遞歸的那個函數(shù)。這個函數(shù)實現(xiàn)如下:

    function toFactorial2(n,total){
        if(n == 1) return total;
        return toFactorial2.bind(null,n-1,n*total);
    }

toFactorial2函數(shù)的返回值是toFactorial2使用bind方法綁定的另一個函數(shù)驼侠。這樣每次循環(huán)函數(shù)使得前一個toFactorial2 能出棧姿鸿。這樣就不會造成棧溢出。

接下來我們使用這個函數(shù)倒源。

trampoline(toFactorial2(1000000,1)) //Infinity 

(2). 還有另外一種方式苛预。

function tco(f) {
  var value;
  var active = false;
  var accumulated = [];

  return function accumulator() {
    accumulated.push(arguments);
    if (!active) {
      active = true;
      while (accumulated.length) {
        value = f.apply(this, accumulated.shift());
      }
      active = false;
      return value;
    }
  };
}

var sum = tco(function(x, y) {
  if (y > 0) {
    return sum(x + 1, y - 1)
  }
  else {
    return x
  }
});

sum(1, 100000)

這個函數(shù)才是真正的尾遞歸優(yōu)化,對于尾遞歸優(yōu)化笋熬,讀者朋友們可以去看阮一峰老師對尾調用以及尾遞歸的實現(xiàn)的講解热某。里面詳解很到位。以上內容是我對javscript調用棧以及遞歸的簡單記錄突诬。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末苫拍,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子旺隙,更是在濱河造成了極大的恐慌绒极,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蔬捷,死亡現(xiàn)場離奇詭異垄提,居然都是意外死亡,警方通過查閱死者的電腦和手機周拐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進店門铡俐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人妥粟,你說我怎么就攤上這事审丘。” “怎么了勾给?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵滩报,是天一觀的道長锅知。 經常有香客問我,道長脓钾,這世上最難降的妖魔是什么售睹? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮可训,結果婚禮上昌妹,老公的妹妹穿的比我還像新娘。我一直安慰自己握截,他們只是感情好飞崖,可當我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布谨胞。 她就那樣靜靜地躺著蚜厉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪畜眨。 梳的紋絲不亂的頭發(fā)上昼牛,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天,我揣著相機與錄音康聂,去河邊找鬼贰健。 笑死,一個胖子當著我的面吹牛恬汁,可吹牛的內容都是我干的伶椿。 我是一名探鬼主播,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼氓侧,長吁一口氣:“原來是場噩夢啊……” “哼脊另!你這毒婦竟也來了?” 一聲冷哼從身側響起约巷,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤偎痛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后独郎,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體踩麦,經...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年氓癌,在試婚紗的時候發(fā)現(xiàn)自己被綠了谓谦。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡贪婉,死狀恐怖反粥,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤才顿,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布践剂,位于F島的核電站,受9級特大地震影響娜膘,放射性物質發(fā)生泄漏。R本人自食惡果不足惜优质,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一竣贪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧巩螃,春花似錦演怎、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至拍皮,卻和暖如春歹叮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背铆帽。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工咆耿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人爹橱。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓萨螺,卻偏偏與公主長得像,于是被迫代替她去往敵國和親愧驱。 傳聞我的和親對象是個殘疾皇子慰技,可洞房花燭夜當晚...
    茶點故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內容

  • 什么是尾調用? 尾調用(Tail Call)是函數(shù)式編程的一個重要概念组砚,本身非常簡單吻商,一句話就能說清楚,就是指某個...
    alex夏夜閱讀 1,824評論 0 3
  • 尾調用之所以與其他調用不同糟红,就在于它的特殊調用位置 一般來說手报,函數(shù)調用會在內存形成一個“調用記錄”,又稱“調用幀”...
    nomooo閱讀 869評論 0 7
  • 函數(shù)參數(shù)的默認值 基本用法 在ES6之前改化,不能直接為函數(shù)的參數(shù)指定默認值掩蛤,只能采用變通的方法。 上面代碼檢查函數(shù)l...
    呼呼哥閱讀 3,363評論 0 1
  • 調用棧(Call Stack) 調用棧(Call Stack)是一個基本的計算機概念,這里引入一個概念:棧幀。 棧...
    SherHoooo閱讀 4,197評論 2 14
  • 希望我們能好好的努力阳藻, 腳踏實地的走好每一步晰奖, 去想去的地方, 見想見的人腥泥, 做喜歡做的事情匾南, 有星辰大海, 也有...
    艾米_2803閱讀 265評論 0 0