讓函數進化

本文同步自個人 GitHubjavascript

假設如今有這樣一個場景,一個下單頁面,須要根據特定的條件去計算購物車商品的總價,特定的條件包括但不限於選中的商品、是否選擇折扣、是否疊加套餐和運費險等。這時咱們一般須要寫一個用來計算總價的函數getTotalPrice(),然而,函數的參數是不肯定的,同時函數內的邏輯也會根據參數的不一樣而有所變化,最簡單的方法是在函數內部對參數進行判斷:java

若是參數分開傳入,那麼函數的僞代碼可能寫成這樣:git

function getTotalPrice(selectedGoods, isDiscounted, pacakge) {
    var totalPrice = 0;
    if (selectedGoods) {
        totalPrice = ...;
        if (isDiscounted) {
            // 計算折扣
            if (package) {...}
        } else if (pacakge) {...}
        return totalPrice;
    } else {
        return 0;
    }
}

有的人可能以爲將參數分開傳入函數看起來比較亂,會習慣用一個參數對象包裹一下,在函數內部再對這個對象進行解析。看起來確實有必定的改進,但我認爲並無什麼本質上的區別。github

如今但願可以作到針對不一樣參數的狀況,在函數內部只書寫針對特定個數參數的邏輯,不去寫亂七八糟的if-else語句或是參數對象的解析邏輯。同時,這些函數共用一個函數名,而且該邏輯可擴展,便可以隨時根據須要,添加函數。緩存

經過對需求的分析,應該會有這樣的一個工具函數:數據結構

var methods = {};

addFunction(methods, 'getTotalPrice', function(selectedGoods) {...});
addFunction(methods, 'getTotalPrice', function(selectedGoods, isDiscounted) {...});
addFunction(methods, 'getTotalPrice', function(selectedGoods, isDiscounted, pacakge) {...});

這樣,在添加每一個函數的時候,只須要寫針對特定參數的邏輯,調用getTotalPrice的時候,自動根據參數長度實際調用不一樣的函數。閉包

很容易想到要對不一樣的函數進行緩存,同時爲了公用同一個函數名,緩存的函數須要匿名,進而聯想到閉包能夠保存局部變量的引用的特性,如下是addFunction的實現:app

function addFunction(object, funcName, fn) {
    var oldFunc = object[funcName];
    object[funcName] = function() {
        return (fn.length === arguments.length ? fn : oldFunc).apply(this, arguments);
    }
}

addFunction方法的做用域中,保存了原方法的引用,經過對參數長度的比較肯定最終執行哪一個匿名函數,要注意的是一個函數的length屬性表示改函數接受的形參數量,而arguments.length表示實際傳入的參數數量。匿名函數訪問oldFuncfn則是典型的閉包的應用,並無把函數存儲在任何典型的數據結構中。函數

addFunction(methods, 'getTotalPrice', function(total) {
    console.log(total);
});
addFunction(methods, 'getTotalPrice', function(total, discount) {
    console.log(total * discount);
});

methods.getTotalPrice(20); // 輸出20
methods.getTotalPrice(20, 0.8); // 輸出16

要注意的是,這種方式只能針對不一樣數量的參數,不能判斷參數的名稱或類型,若是要判斷的話,勢必會犧牲addFunction方法的通用性。另外,每添加一個函數,就會增長一層嵌套,調用深度過深的話性能開銷也會比較大,使用的時候要進行權衡。工具

相關文章
相關標籤/搜索