作為函數式編程語言,函數在JavaScript中的重要地位和巨大作用你盡可想象。但在其提供巨大便利的同時,也不可避免的帶來巨大的問題。 匿名函數則更是一把雙刃劍,它讓函數式編程語言更加完美,也讓代碼更加難於閱讀。你應該知道匿名函數是以犧牲語義化為巨大代價的。 如果一個函數沒有名字,它可能無關緊要,在 ...
作為函數式編程語言,函數在JavaScript中的重要地位和巨大作用你盡可想象。但在其提供巨大便利的同時,也不可避免的帶來巨大的問題。
匿名函數則更是一把雙刃劍,它讓函數式編程語言更加完美,也讓代碼更加難於閱讀。你應該知道匿名函數是以犧牲語義化為巨大代價的。
如果一個函數沒有名字,它可能無關緊要,在大部分場景中它都將失去意義。函數的名字跟你的名字,你朋友的名字,你家小寵物的名字一樣,是重要關鍵的,否則你寫它幹嘛。
即使在看起來最沒有必要的位置使用命名函數也有巨大價值
你可能隨手就能枚舉很多場景來證明匿名函數的便利性,不可否認有些場景無疑是有一些道理的,但大多數人以此為起點,以善小而不為。比如
Array.some, Array.forEach, String.replace
這樣的例子能舉出不少,我們也可以理直氣壯的說他們不需要使用命名函數,使用匿名函數更方便,並且大家都是這麼做的。但是別忘了,some,forEach,replace本身已經具有寬泛的語義了。但仍然可以更近一步:
1.Array.some, 廣泛的語義是找出數組中是否有一些,但究竟有一些什麼呢?
2.Array.forEach, 廣泛的語義是遍歷,Array提供無數的函數用於遍歷,你為什麼選擇forEach, 而不是map,every等等?
3.String.replace, 廣泛的語義是替換,但究竟是將什麼替換成什麼呢?
代碼是寫給人看的,
1.能不能不要讓我去讀你的代碼猜或者推理出來你要從數組中找出一些什麼,是不是有整數,是不是有空值?
2.能不能直接通過函數名告訴我你遍歷這個數組是想乾什麼?
3.能不能直接通過函數名告訴我你想將什麼替換成什麼?
舉個簡單的例子,找出價格數組中是否存在整數價格。isInteger可能是已經有的公共函數,如果沒有的話,經過你就有了。簡潔易讀,在閱讀主流程的時候,有些代碼是不需要閱讀的,isInteger就是這樣一些代碼,萬一有錯呢?isInteger是可測易測的,如果你不放心,對它執行單元測試。你可能已經註意到,一個小小的改變,有一部分代碼已經具有可測性了。即使這裡不是一個公用函數,寫成命名函數也更整潔更可測。
var isAnyInteger = priceArr.some(Common.isInteger.bind(Common));
這些都是非常極端的被認為是可以直接寫匿名函數的例子,但很明顯可以看到,他們也可以作為命名函數的邊界處理,即都寫命名函數百利而無一害,只會更好。
再簡單的代碼也要區分架構和實現
另外一個重災區是 then 函數,匿名函數的代碼不能更醜陋,即使大家都這麼寫,你也應該明白,你不能這麼寫,正確的姿勢應該寫成這樣, 以展示訂單為例:
/* * name : getOrder * description : 獲取訂單數據 */ function getOrder() {//{{{ var url = 'https://www.qunar.com/getOrder'; //假如收集參數比較費勁,應該用一個函數專門去收集參數 var params = getOrderParams(); //假如參數體比較龐大,應該先將其賦予一個變數 var params = { orderNo:'248663058' }; //無論如何,現將參數賦予變數你都將獲得在這裡列印變數方便調試的便利 console.log('getOrder url & params:', url, params); return $.post(url, params); }//}}} /* * name : renderOrder * description : 拿訂單,拿到就在頁面上展示出來,拿不到就告訴用戶為什麼沒拿到 */ function renderOrder() {//{{{ //高級函數,只安排工作,不自己實現 //getOrder() 對該函數來說是不可見的,它要的只是訂單數據,偷得搶的都可以 getOrder().then(render, remindUser); }//}}} function render() {//{{{ }//}}} function remindUser() {//{{{ }//}}}
當然,你可能覺得 renderOrder 就是雞肋,跟只拿工資不幹活的領導簡直一毛一樣,把then寫在 $.post 後面不就行了?
不行,如果有一天獲取訂單既可以從本地獲取,又可以從本地緩存獲取,那麼getOrder就升級為次高級函數,它管理兩個函數 getOrderFromServer, getOrderFromCache
如果在獲取訂單之前或者之後還要做一些事情,那之後renderOrder能夠從容應付。
想都不要想過度設計這個詞,大部分人沒有這個能力,不必杞人憂天。
你從一開始就是高級架構師只不過兼職寫代碼
你可以清楚的看到,大量使用命名函數讓代碼結構非常清晰,任何人都能容易的獨懂主流程,任何人都可以容易的去實現每一個命名函數,要實現什麼已經清楚的寫在了函數名上了。
這些命名函數就像房屋的骨架,再往上堆疊就是一棟樓的骨架,你見過建築設計師自己砌牆澆水泥的嗎?寫代碼也一樣,用函數堆疊成骨架,至於每個函數怎麼實現,請幫我實現(在你初學的時候就是請你自己幫你自己實現)。