一起來啃《JavaScript語言精粹》----函式

amadan發表於2021-09-09

一、呼叫

  • 方法呼叫模式
    當一個函式被儲存為物件的一個屬性時,我們稱它為一個方法。如果呼叫表示式包含一個提取屬性的動作,那麼它就是被當做一個方法來呼叫。

圖片描述

Paste_Image.png


方法可以使用this訪問自己所屬的物件,透過this可取得它們所屬物件的上下文的方法稱為公共方法。

  • 函式呼叫模式
    當一個函式並非一個物件的屬性時,那麼它就是被當做一個函式來呼叫:
    var sum = add(3, 4);//7
    以此模式呼叫函式時,this被繫結到全域性物件。


    圖片描述

    Paste_Image.png


    這事語言設計的一個錯誤,錯誤後果就是方法不能利用內部函式來幫助它工作,因為內部函式的this被繫結了錯誤的值,所以不能共享該方法對物件的訪問權。
    解決方案:
    為該方法定義一個變數並給它賦值為this,那麼內部函式就可以透過那個變數訪問到this:


    圖片描述

    Paste_Image.png

  • 構造器呼叫模式
    如果在一個函式前面帶上 new 來呼叫,那麼背地裡將會建立一個連線到該函式的prototype成員的新物件,同時 this 會被繫結到那個新物件上。
    //建立一個名為Quo的構造器函式。它構造一個帶有status屬性的物件。
    var Quo = function(string){
    this.status = string
    }
    //給Quo的所有例項提供一個名為get_status的公共方法。
    Quo.prototype.get_status = function(){
    return this.status;
    };
    //構造一個Quo例項。
    var myQuo = new Quo('confused');
    myQuo.get_status()//'confused'

  • Apply呼叫模式
    apply方法讓我們構建一個引數陣列傳遞給呼叫函式。它也允許我們選擇this的值。apply方法接收兩個引數,第一個是要繫結給this的值,第二個就是一個引數陣列。

圖片描述

Paste_Image.png

二、擴充型別的功能

  • 我們可以透過給Function.prototype 增加方法來使得該方法對所有函式可用


    圖片描述

    Paste_Image.png


    透過給Function.prototype 增加一個method方法,我們下次給物件增加方法的時候就不必鍵入prototype這幾個字元,省掉了一點麻煩。

  • JavaScript沒有專門的整數型別,但有時候確實只需要提取數字中的整數部分。JavaScript本身提供的取整方法有些醜陋。我們可以透過Number.prototype增加一個integer方法來改善它。它會根據數字的正負來判斷是使用Math.ceiling還是Math.floor。


    圖片描述

    Paste_Image.png

  • PS:這裡需要指出的是Javascript的原型繼承機制,可以形象的理解為Object是祖師爺爺,Function既當爹又當媽,剩下的Number,String,Array等都是兒女:
    Number,String,Array => Function => Object

圖片描述

Paste_Image.png

  • 另外,基本型別的原型是公用結構,所以在類庫混用時務必小心。一個保險的做法是隻在確定沒有該方法時才新增它。
    //符合條件時才增加方法。
    Function.prototype.method = function(name,func){
    if(!this.prototype[name]){
    this.prototype[name] = func;
    }
    return this;
    }

三、閉包

  • 我們透過呼叫一個函式的形式去初始化myObject,該函式會返回一個物件字面量。函式里定義一個value變數。該變數對increment和getValue方法總是可用的,但是函式的作用域使得它對其他的程式來說是不可見的。


    圖片描述

    Paste_Image.png


    這個例子我們並沒有把一個函式賦值給myObject。我們是把呼叫該函式後返回的結果賦值給它。

圖片描述

Paste_Image.png


當我們呼叫quo時,它返回包含get_status方法的一個新物件。該物件的一個引用儲存在myQuo中。即使quo已經返回了,但get_status方法仍然享有訪問quo物件的status屬性的特權。get_status方法並不是訪問該引數的一個副本,它訪問的就是該引數本身。這是可能的,因為該函式可以訪問它被建立時所處的上下文環境,這被稱為閉包

  • 來看一個更有用的例子


    圖片描述

    Paste_Image.png


    1.我們呼叫fade,把document.body作為引數傳遞給它,fade函式設定level為1.它定義一個step函式,接著呼叫setTimeout,並傳遞step函式和一個時間(100毫秒)給它。然後它返回,fade函式結束。
    2.在大約100毫秒後,step函式被呼叫。它把fade函式的level變數轉換為16位字元。接著,它修改fade函式得到的節點的背景顏色。然後檢視fade函式的level變數。如果背景色尚未變成白色,那麼它增大fade函式的level變數,接著用setTimeout預定讓它自己再次執行。
    3.step函式很快再次被呼叫。但這次,fade函式的level變數值變成2.fade函式在之前已經返回了,但只要fade的內部函式需要,它的變數就會持續保留。

  • 從一個經典的錯誤說起
    構造六個div,當點選一個div時,按照預期,應該打出相應div的值,但是它總是會顯示div的數量。因為事件處理器函式繫結了變數i本身,而不是函式在構造時的變數i的值,i始終無法被釋放。

圖片描述

Paste_Image.png


避免在迴圈中建立函式,它可能只會帶來無謂的計算,還會引起混淆,正如上面那個經典的錯誤。我們可以先在迴圈外建立一個輔助函式,讓這個輔助函式再返回一個繫結了當前i值的函式,這樣就不會導致混淆了。改良後的例子,用正確的方式給一個陣列中的節點設定事件處理程式


圖片描述

Paste_Image.png


另一種解決辦法,這裡我們用一個立即執行函式給它包住,我們不再依賴i,而是用另外一個變數n把它保留下來。

圖片描述

Paste_Image.png

四、記憶

  • 函式可以將先前操作的結果記錄在某個物件裡,從而避免無謂的重複運算。這種最佳化被稱為記憶。JavaScript的物件和陣列要實現這種最佳化是非常方便的。
    比如說,我們想要一個遞迴函式來計算Fibonacci數列。一個Fibonacci數列是之前兩個Fibonacci數字之和。最前面的兩個數字是0和1。


    圖片描述

    Paste_Image.png


    但是這樣做了很多無謂的工作。fibonacci函式被呼叫了453次。如果我們讓該函式具備記憶功能,就能顯著地減少運算量。
    我們在一個名為memo的陣列裡儲存我們的儲存結果,儲存結果可以隱藏在閉包中。當函式被呼叫時,這個函式首先檢查結果是不是已經存在,如果存在,就立即返回這個結果。


    圖片描述

    Paste_Image.png


    這個函式返回的結果相同,但是它只被呼叫了29次。

  • 我們可以編寫一個函式來幫助我們構造帶記憶功能的函式。memoizer函式取得一個初始的memo陣列和formula函式。它返回一個管理meno儲存和在需要時呼叫formula函式的recur函式。我們把這個recur函式和它的引數傳遞給formula函式:


    圖片描述

    Paste_Image.png


    現在,我們可以使用memoizer函式來定義fibonacci函式,提供其初始的memo陣列和formula函式:


    圖片描述

    Paste_Image.png


    透過設計這種產生另一個函式的函式,極大地減少了我們的工作量。例如,要產生一個可以記憶的階乘函式,我們只需提供基本的階乘公式即可:

    圖片描述

    Paste_Image.png



作者:韓寶億
連結:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2310/viewspace-2812276/,如需轉載,請註明出處,否則將追究法律責任。

相關文章