本文同步自個人 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
表示實際傳入的參數數量。匿名函數訪問oldFunc
和fn
則是典型的閉包的應用,並無把函數存儲在任何典型的數據結構中。函數
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
方法的通用性。另外,每添加一個函數,就會增長一層嵌套,調用深度過深的話性能開銷也會比較大,使用的時候要進行權衡。工具