亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > JS > 正文

js尾調用優化的實現

2024-05-06 16:51:17
字體:
來源:轉載
供稿:網友

尾調用(Tail Call)是函數式編程的一個重要概念,本文介紹它的含義和用法。

一、什么是尾調用?

尾調用的概念非常簡單,一句話就能說清楚,就是指某個函數的最后一步是調用另一個函數。

function f(x){ return g(x);}

上面代碼中,函數f的最后一步是調用函數g,這就叫尾調用。

以下兩種情況,都不屬于尾調用。

// 情況一function f(x){ let y = g(x); return y;}// 情況二function f(x){ return g(x) + 1;}

上面代碼中,情況一是調用函數g之后,還有別的操作,所以不屬于尾調用,即使語義完全一樣。情況二也屬于調用后還有操作,即使寫在一行內。

尾調用不一定出現在函數尾部,只要是最后一步操作即可。

function f(x) { if (x > 0) {  return m(x) } return n(x);}

上面代碼中,函數m和n都屬于尾調用,因為它們都是函數f的最后一步操作。

二、尾調用優化

尾調用之所以與其他調用不同,就在于它的特殊的調用位置。

我們知道,函數調用會在內存形成一個"調用記錄",又稱"調用幀"(call frame),保存調用位置和內部變量等信息。如果在函數A的內部調用函數B,那么在A的調用記錄上方,還會形成一個B的調用記錄。等到B運行結束,將結果返回到A,B的調用記錄才會消失。如果函數B內部還調用函數C,那就還有一個C的調用記錄棧,以此類推。所有的調用記錄,就形成一個"調用棧"(call stack)。

js,尾調用

尾調用由于是函數的最后一步操作,所以不需要保留外層函數的調用記錄,因為調用位置、內部變量等信息都不會再用到了,只要直接用內層函數的調用記錄,取代外層函數的調用記錄就可以了。

function f() { let m = 1; let n = 2; return g(m + n);}f();// 等同于function f() { return g(3);}f();// 等同于g(3);

上面代碼中,如果函數g不是尾調用,函數f就需要保存內部變量m和n的值、g的調用位置等信息。但由于調用g之后,函數f就結束了,所以執行到最后一步,完全可以刪除 f() 的調用記錄,只保留 g(3) 的調用記錄。

這就叫做"尾調用優化"(Tail call optimization),即只保留內層函數的調用記錄。如果所有函數都是尾調用,那么完全可以做到每次執行時,調用記錄只有一項,這將大大節省內存。這就是"尾調用優化"的意義。

三、尾遞歸

函數調用自身,稱為遞歸。如果尾調用自身,就稱為尾遞歸。

遞歸非常耗費內存,因為需要同時保存成千上百個調用記錄,很容易發生"棧溢出"錯誤(stack overflow)。但對于尾遞歸來說,由于只存在一個調用記錄,所以永遠不會發生"棧溢出"錯誤。

function factorial(n) { if (n === 1) return 1; return n * factorial(n - 1);}factorial(5) // 120

上面代碼是一個階乘函數,計算n的階乘,最多需要保存n個調用記錄,復雜度 O(n) 。

如果改寫成尾遞歸,只保留一個調用記錄,復雜度 O(1) 。

function factorial(n, total) { if (n === 1) return total; return factorial(n - 1, n * total);}factorial(5, 1) // 120

js,尾調用

由此可見,"尾調用優化"對遞歸操作意義重大,所以一些函數式編程語言將其寫入了語言規格。ES6也是如此,第一次明確規定,所有 ECMAScript 的實現,都必須部署"尾調用優化"。這就是說,在 ES6 中,只要使用尾遞歸,就不會發生棧溢出,相對節省內存。

四、遞歸函數的改寫

尾遞歸的實現,往往需要改寫遞歸函數,確保最后一步只調用自身。做到這一點的方法,就是把所有用到的內部變量改寫成函數的參數。比如上面的例子,階乘函數 factorial 需要用到一個中間變量 total ,那就把這個中間變量改寫成函數的參數。這樣做的缺點就是不太直觀,第一眼很難看出來,為什么計算5的階乘,需要傳入兩個參數5和1?

兩個方法可以解決這個問題。方法一是在尾遞歸函數之外,再提供一個正常形式的函數。

function tailFactorial(n, total) { if (n === 1) return total; return tailFactorial(n - 1, n * total);}function factorial(n) { return tailFactorial(n, 1);}factorial(5) // 120

上面代碼通過一個正常形式的階乘函數 factorial ,調用尾遞歸函數 tailFactorial ,看起來就正常多了。

函數式編程有一個概念,叫做柯里化(currying),意思是將多參數的函數轉換成單參數的形式。這里也可以使用柯里化。

function currying(fn, n) { return function (m) {  return fn.call(this, m, n); };}function tailFactorial(n, total) { if (n === 1) return total; return tailFactorial(n - 1, n * total);}const factorial = currying(tailFactorial, 1);factorial(5) // 120

上面代碼通過柯里化,將尾遞歸函數 tailFactorial 變為只接受1個參數的 factorial 。

第二種方法就簡單多了,就是采用ES6的函數默認值。

function factorial(n, total = 1) { if (n === 1) return total; return factorial(n - 1, n * total);}factorial(5) // 120

上面代碼中,參數 total 有默認值1,所以調用時不用提供這個值。

總結一下,遞歸本質上是一種循環操作。純粹的函數式編程語言沒有循環操作命令,所有的循環都用遞歸實現,這就是為什么尾遞歸對這些語言極其重要。對于其他支持"尾調用優化"的語言(比如Lua,ES6),只需要知道循環可以用遞歸代替,而一旦使用遞歸,就最好使用尾遞歸。

([說明] 本文摘自我寫的《ECMAScript 6入門》)

五、嚴格模式

ES6的尾調用優化只在嚴格模式下開啟,正常模式是無效的。

這是因為在正常模式下,函數內部有兩個變量,可以跟蹤函數的調用棧。

  • arguments:返回調用時函數的參數。
  • func.caller:返回調用當前函數的那個函數。

尾調用優化發生時,函數的調用棧會改寫,因此上面兩個變量就會失真。嚴格模式禁用這兩個變量,所以尾調用模式僅在嚴格模式下生效。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。


注:相關教程知識閱讀請移步到JavaScript/Ajax教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
精品国产户外野外| 成人免费网视频| 国产精品99久久久久久久久久久久| 国产精品国产三级国产专播精品人| 欧美在线欧美在线| 欧美大荫蒂xxx| 亚洲欧美日韩国产中文| 日韩精品视频免费| 久久视频在线直播| 国产成人亚洲综合青青| 麻豆乱码国产一区二区三区| 国产丝袜一区视频在线观看| 伊人久久精品视频| 国产精品999999| 在线不卡国产精品| 在线观看欧美日韩国产| 精品电影在线观看| 成人黄色生活片| 91网站在线免费观看| 亚洲精品v天堂中文字幕| 欧美精品在线播放| 欧美精品成人在线| 国产精品女人久久久久久| 欧美影院成年免费版| 91sao在线观看国产| 亚洲激情成人网| 久久精品国产一区二区三区| 日韩大片在线观看视频| 国产精品稀缺呦系列在线| 国产精品美女久久久久av超清| 国产精品香蕉av| 精品性高朝久久久久久久| 久久久精品视频在线观看| 中文字幕日韩在线播放| 日韩免费中文字幕| 亚洲欧美综合图区| 最近中文字幕mv在线一区二区三区四区| 精品亚洲一区二区三区四区五区| 欧美插天视频在线播放| 亚洲国内高清视频| 欧美超级免费视 在线| 国产精品欧美激情在线播放| 欧日韩不卡在线视频| 国产有码在线一区二区视频| 国产成一区二区| 色悠悠久久久久| 亚洲成人网久久久| 午夜精品99久久免费| 92版电视剧仙鹤神针在线观看| 欧美另类99xxxxx| 久久精品91久久香蕉加勒比| 久久99国产精品久久久久久久久| 海角国产乱辈乱精品视频| 亚洲精品视频免费在线观看| 日韩精品福利在线| 欧美精品一区三区| 国产精品劲爆视频| 亚洲www在线观看| 国产成人综合亚洲| 国语自产在线不卡| 国产日韩欧美在线播放| 国产成人综合精品在线| 91国自产精品中文字幕亚洲| 亚洲精品国产suv| 欧美精品电影免费在线观看| 亚洲aⅴ男人的天堂在线观看| 亚洲aⅴ男人的天堂在线观看| 一本色道久久88亚洲综合88| 日韩在线国产精品| 亚洲精品aⅴ中文字幕乱码| 91tv亚洲精品香蕉国产一区7ujn| 亚洲网站在线看| 欧洲美女7788成人免费视频| 亚洲最大激情中文字幕| 伊人久久久久久久久久久| 91久热免费在线视频| 亚洲永久在线观看| 欧美亚洲另类在线| 91av中文字幕| 亚洲精品自在久久| 欧美性猛交xxxxx免费看| 久久国产精彩视频| 日韩精品中文字幕久久臀| 国产中文字幕亚洲| 色777狠狠综合秋免鲁丝| 久久亚洲国产精品成人av秋霞| 国产日韩精品在线播放| 成人精品久久一区二区三区| 成人春色激情网| 96sao精品视频在线观看| 欧美风情在线观看| 91在线免费看网站| 亚洲欧美资源在线| 人人做人人澡人人爽欧美| 69久久夜色精品国产7777| 少妇久久久久久| 国产91亚洲精品| 亚洲丝袜一区在线| 日韩动漫免费观看电视剧高清| 国产一区二区三区欧美| 久久久人成影片一区二区三区| 精品视频9999| 亚洲成av人片在线观看香蕉| 国产专区欧美专区| 日韩在线观看网址| 久久亚洲精品一区| 成人在线免费观看视视频| 亚洲最大的免费| 久久久久久国产精品三级玉女聊斋| 国产视频亚洲精品| 国产精品视频午夜| 欧美国产日本高清在线| 国产丝袜高跟一区| 亚洲一级黄色av| 在线观看国产成人av片| 精品人伦一区二区三区蜜桃免费| 成人免费网视频| 精品久久久香蕉免费精品视频| 午夜剧场成人观在线视频免费观看| 日韩最新av在线| 中文字幕视频一区二区在线有码| 成人av在线亚洲| 国产一区二区三区日韩欧美| 97av视频在线| 精品国产欧美一区二区三区成人| 91在线视频免费| 亚洲福利在线观看| 欧美成人中文字幕| 国产精品欧美激情| 亚洲精品久久久久久下一站| 午夜精品一区二区三区在线视频| 国产欧美日韩精品丝袜高跟鞋| 国产精品亚洲网站| 日韩亚洲第一页| 欧美国产一区二区三区| 日韩中文娱乐网| 国产精品视频地址| 欧美一区二三区| 亚洲成人激情在线观看| 欧美日韩人人澡狠狠躁视频| 亚洲人成电影在线| 亚洲第一福利网站| 久久久精品美女| 福利精品视频在线| 国产自产女人91一区在线观看| 26uuu日韩精品一区二区| 91精品国产91久久久久久不卡| 欧美性极品少妇精品网站| 亚洲精品xxx| 91av在线影院| 国内伊人久久久久久网站视频| 国产日韩av高清| 俺去了亚洲欧美日韩| 国产精品69久久| 日本亚洲精品在线观看| 亚洲国产日韩欧美综合久久| 欧美老女人在线视频| 欧美日韩午夜视频在线观看| 亚洲片国产一区一级在线观看| 成人午夜激情免费视频| 亚洲视频axxx| 神马久久桃色视频| 久久天堂av综合合色| 国产日韩中文字幕在线|