javascript一等公民------函式

飛雨軒發表於2018-07-29

1.函式的概念

javascript中最關鍵的概念:函式是第一類物件,或稱它們為一等公民。
複製程式碼

函式也可以被視為其他任意型別的javascript物件。javascript中函式擁有物件的所有能力,頁因此函式可被作為任意其他型別物件來對待。當我們說函式是第一類物件時就是說函式也能夠實現以下功能:

  • 1.通過字面量建立,陣列項或其他物件的屬性。
  • 2.賦值給變數,陣列或其他物件的而屬性。
  • 3.作為函式的引數傳遞。
  • 4.作為函式的返回值。
  • 5.具有動態建立和分配的屬性。

物件能做的任何一件事,函式也能做。函式也是物件,唯一的特殊之處在於它是可呼叫的,即函式會被呼叫以便執行某項操作。

2.回撥函式

每當我們建立一個將在隨後呼叫的函式時,無論是在事件處理階段通過瀏覽器還是通過其他程式碼,我們都是在建立一個回撥(callback)。這個術語源自於這樣一個事實,即在執行過程中,我們建立的函式會在稍後的某個合適時間點“再回來呼叫”。

function useless(callback){
    return callback();
}
複製程式碼

這個函式可能沒什麼用,但它反應了函式的一種能力,即將函式作為另一個函式的引數,隨後通過引數來呼叫------這個也就是上面所說的函式的第三個功能!

var text="Domo arigato!";
report("Before defining functions");

//函式定義,引數作為一個回撥函式,其函式體內會立即呼叫該回撥函式
function useless(callback){
    report("In useless function");
    return callback();
}

//簡單的函式定義,僅返回一個全域性變數
function getText(){
    report("In getText function");
    return text;
}
report("Before making all the calls");

//把getText作為回撥函式傳入上面的useless函式
assert(useless(getText)===text,
        "The useless function works!"+text);
report("After the calls have been made!")
複製程式碼

javascript的重要特性之一是可以在表示式出現的任意位置建立函式,除此之外這種方式能使程式碼更緊湊和易於理解(把函式定義放在函式使用處附近)。當一個函式不會在程式碼的多處位置被呼叫時,該特性可以避免用非必需的名字汙染全域性命名環境。

document.body.addEventListener("mousemove",function(){
    var second=document.getElementById("second");
    addMessage(second,"Event:mousemove");
});
複製程式碼

上述同樣是一個回撥函式的例子,作為mousemove事件的事件處理器,當事件發生時,會被瀏覽器呼叫。

綜上:回撥函式是其程式碼會在隨後的某個合適時間點“回過來呼叫”的函式。
複製程式碼

3.儲存函式

某些例子中(例如,我們需要管理某個事件發生後需要呼叫的回撥函式集合),我們會儲存元素唯一的函式集合。當我們向這樣的集合中新增函式時,會面臨兩個問題:哪個函式對於這個集合來說是一個新函式,從而需要被加入到該集合中?又是哪一個函式已經存在於集合中,從而不需要再次加入到集合中?一般來說,管理回撥函式集合時,我們並不希望存在重複函式,否則一個事件會導致同一回撥函式被多次呼叫。

給函式附加一個屬性後,我們就可以引用該屬性。下面的例子就是通過該方法確保ninja函式僅被新增到函式中一次。  
複製程式碼
var store={
    nextId:1,       //跟蹤下一個要被複制的函式
    cache:{},       //使用一個物件作為快取,我們可以在其中儲存函式
    add:function(){         
        if(!fn.id){         //僅當函式唯一時,將該函式加入快取
            fn.id=this.nextId++;
            this.cache(fn.id)=fn;
            return true;
        }
    }
};
function ninja(){};

//測試上面程式碼按預期工作
assert(store.add(ninja),
        "Function was safely added.");
assert(!store.add(ninja),
        "But it was only added once.");
複製程式碼

另外一種有用的技巧是當使用函式屬性時,可以通過該屬性修改函式自身。這個技術可以用於記憶前一個計算得到的值,為之後計算節省時間。

4.自記憶函式

記憶化是一種構建函式的處理過程,能夠記住上次計算結果。在這個外殼中,當函式計算得到結果時就將該結果按照引數儲存起來。採用這種方式,如果另外一個呼叫也使用相同的引數,我們則可以直接返回上次儲存的結果而不是再計算一遍,這樣既避免重複又複雜的計算也可以顯著的提高效能。(記憶化特指函式的返回值)

下面的例子使用一個簡單的演算法計算素數,儘管這是一個複雜計算的簡單例子,但它經常被應用到大計算量的場景中。



function isPrime(val){
    //建立快取
    if(!isPrime,answers){
        isPrime.answers={};
    }
    //檢查快取的值
    if(isPrime.answers[val] !== undefined){
        return isPrime.answers[val];
    }
    var prome = val !== 0 && val !==1;  //1 is not a prime
    for (var i = 2;i<val;i++){
        if(val % i ===0){
            prime = false;
            break;
        }
    }
    return isPrime.answer[val] = prime;  //<--------儲存計算的值
}
//測試該函式是否正常工作
assert(isPrime(5),"5 is prime!");
assert(isPrime.answers[5],"The answer was cached!");
複製程式碼

這個方法有兩個優點:

  • 1.由於函式呼叫時會尋找之前呼叫所得到的值,所以使用者最終會樂於看到所獲得的效能收益;
  • 2.它幾乎是無縫地發生在後臺,終端使用者和頁面作者都不需要執行任何特殊請求,也不需要做任何額外初始化,就能順利進行工作。

當然這種方法並不是像玫瑰和提琴一樣完美,還是要權衡利弊,任何型別的快取都必然會為效能犧牲記憶體!

5.立即函式

函式表示式可以放在初看起來有些奇怪的位置上,例如通常認為是函式識別符號的位置。接下來仔細看看這個構造:

javascript一等公民------函式

在javascript庫中會經常見到這幾種形式:

  • 1.+function(){}();
  • 2.-function(){}();
  • 3.!function(){}();
  • 4.~function(){}();

不同於用括號的方式區分函式表示式和函式宣告,這裡我們使用一元操作符+,-,!和~。這種做法也是用於向javascript引擎指明它處理的是表示式,而不是語句。

相關文章