JavaScript入門-函式function(二)

lovelyk 發表於 2021-01-17

JavaScript入門-函式function(二)

遞迴函式

  • 什麼是遞迴函式?
    遞迴簡單理解就是,在函式體裡,呼叫自己。
    //我們在求一個10的階乘的時候,可能會這麼做
    //寫一個迴圈
    var total = 1
    for(var i=10; i>=1; i++){
        total *= i ;
    }
    這樣的for迴圈簡單直接。。。大家都會寫
    但是,我們可以寫一個函式,遞迴呼叫
    //遞迴函式
    function jiecheng( n ){
        if ( 1===n) return 1
        return n*jiecheng(n-1)
    }
    //呼叫遞迴函式
    var total = jiecheng(10)

ps:兩種不同的方法,雖然結果都相同,但是還是有區別的。遞迴併沒有通過for迴圈遍歷每一個數,而且程式碼量變少了,看起來有點高大上。

  • 其實,在遞迴呼叫的時候,就是按照return的特定來執行的。因為return只有在遇到一個具體的值才會返回給呼叫者。
  • 然後,在呼叫的時候,會有一個'棧'的資料結構(先進後出,後進先出),儲存每一個呼叫的時狀態,直到遇到一個能夠有具體值的狀態,就逐層往上返回給呼叫者,最終的值就是你想要的值。

遞迴的缺點

  • 每次呼叫自己的時候,其實一直都是在佔用著記憶體,佔用資源較大。

閉包

  • 什麼是閉包?
    我們先來回顧一下,在es5裡,用var定義的變數,不管是在程式碼塊裡定義,還是在for迴圈裡的區域性變數,我們都能夠訪問到這個變數,這就叫做全域性變數。如果我們在一個函式裡用var定義變數,就是一個區域性變數了,只有內部可以使用。
    //定義全域性變數
    var a =1;

    //函式體定義變數
    function fun(){
        var b =1;
    }

    //程式碼塊裡定義變數
    {
        var c = 1
        {
            var d = 'rainbow'
        }
        
    }
    //for函式裡的變數
    for( var i = 0 ; i<10 ;i++){
        console.log(i)
    }
        console.log( a , c , d , i )//1 1 "rainbow" 10
        console.log(b)//error

ps:上面程式碼可以看出來,在JavaScript裡,除了方法體內的變數未區域性變數,其他的都是全域性變數,那如果我們想要訪問函式裡面的變數該怎麼辦呢??
這個時候,我們就要用到閉包了。

  1. 在函式內自定義一個方法,返回函式定義的區域性變數。
  2. 呼叫的時候,我們先呼叫外層的函式,得到的是一個函式體,然後再一次呼叫,就能夠獲取到outer的內部成員的變數了。
    function outer(){
        var num = 10;

        funtion inner(){
            console.log(num)
        }
        return inner;
    }

    var fun = outer()
    fun()//10

    或者

    function outer() {

        var age = 20;

        return function () {//匿名函式
            return age;
        }
    }
    var age1 = outer();
    console.log(age1())//20

總結:閉包,就是在函式內部定義一個函式,把function裡私有的屬性,通過這個內部函式返回給了外面。
特點:

  1. 通過閉包可以讀取函式內部的變數
  2. 每一次呼叫最外層的方法,實際都是重新開闢一塊記憶體空間., 因為返回的inner方法沒有關閉,一直佔用著記憶體,我們可以手動關閉 outer = null
  3. 在outer裡定義的變數,通過閉包獲取到的成員變數,其實都是靜態static變數,正如第2點所說,變數還是存在記憶體中,並沒有主動釋放。

閉包的缺點

  • 和遞迴類似,一直佔用著記憶體資源,可能導致記憶體洩漏。

可能我自己講的不夠全,大家看看大佬的文章

徹底理解閉包
https://www.cnblogs.com/itjeff/p/10106855.html
阮一峰老師的
http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html

回撥函式

回撥函式有個比較逼格的名字——控制程式碼。我們只要知道有這麼個官方的叫法就行,以後面試或者交流的時候,也不至於聽不懂這是啥東西。
簡單理解就是,把一個函式當作引數。那麼這個函式就是回撥函式。
那為啥叫回撥呢?
那是因為你執行一個方法時候,還不能夠預期或者確定會有什麼樣的結果,必須得再回過頭來呼叫這個函式(也就是傳的引數),得到具體的結果。

    function fun(callback){
        console.log(callback)
    }
    //把一個方法當作引數

    function say (value) {
    alert(value);
    }
    alert(say);
    alert(say('hi js.'));

ps:回撥函式應用場景多用在使用 js 寫元件時,尤其是元件的事件很多都需要回撥函式的支援。

內建函式

其實,我們js的類庫提供了很多的函式,比如經常用的console.log(),alert()等等。但是在還未學習物件導向之前,主要來了解兩個內建函式,定時器setTimeout和setInterval

    //使用方法
    setTimeout(callback,delay);
    setInterval(callback,delay);

    function callback(){
        console.log('rainbow');
    }

    var delay = 1*1000;

    //解釋一下
    callback:定時器要執行的方法
    delay:定時器執行方法的間隔。單位ms毫秒

setTimeout、setInterval區別

  • setTimeout 只能執行一次,而setInterval一直執行。

那麼如何停止定時器呢?
每個定時器都會返回一個定時器id,這個id線上程池中存著,我們接收他的id,然後清除

    //接收id
    var id1 = setTimeout(callback,delay);
    var id2 = setInterval(callback,delay);

    //關閉定時器
    clearTimeout(id1)
    clearInterval(id2)

ps:

  1. 關閉定時器,clearTimeout或者clearInterval都可以關閉對方的id,因為他們共用一個定時器ID poor。
  2. delay這個引數,並不會很精確,因為這和時間片輪轉有關。要想了解更多,可以去學習一下作業系統,程式和執行緒是什麼。

其實,學習定時器的時候,我們就應該瞭解一下,js裡只有單執行緒,而且理論是沒有非同步操作的,大家口中說的那是模擬非同步,那麼大家就需要了解一個叫做任務佇列的東西。
說到這裡,我們就來說三個任務,其實還有很多的:

  • 渲染佇列:比如瀏覽器的渲染,ie的hasLayout
  • 事件佇列:比如點選事件onclick
  • 定時器佇列:一定會等主程式執行完畢後再執行
    這些都是有一定執行順序的,渲染佇列->事件佇列->定時器佇列,所以定時器這個玩意,就像個棄兒,凡事都要最後才能擁有,這也就知道為啥你給他設定delay時間的時候,不會那麼精確了。

謝謝大家能讀完這篇隨筆,鄙人學識淺薄,很多地方講的自認為不是很深入,也比較的俗,見諒見諒...