JavaScript設計原則與編程技巧

1 設計原則概述

  1. UNIX/LINUX設計哲學》設計準則
    ① 小既是美。
    ② 每一個程序只作一件事情。
    ③ 快速創建原型。
    ④ 捨棄高效率而取可移植性。
    ⑤ 避免強制性的圖形化界面交互。
    ⑥ 讓每一個程序都成爲過濾器。
    ⑦ 尋求90%的解決方案。
    註釋:花20%的成本解決80%的需求。
  2. 五大設計原則(SOLID)
    S - 單一職責原則
    O - 開放封閉原則
    L - 李氏置換原則
    I - 接口獨立原則
    D - 依賴倒置原則
  3. 單一職責原則
    一個程序只作好一件事情。
  4. 開放封閉原則
    對擴展開放,對修改封閉。
    增長需求時,擴展新代碼,而非修改已有代碼。
  5. 李氏置換原則
    子類能覆蓋父類。
    父類能出現的地方子類就能出現。
  6. 接口獨立原則
    保持接口的獨立,避免出現「胖接口」。
  7. 依賴倒置原則
    面向接口編程,依賴於抽象而不依賴於具體。

2 單一職責原則

  1. 簡介
    就一個類、對象以及方法而言,應該僅有一個引發它變化的緣由。
    註釋:單一職責原則定義爲「引發變化的緣由」。若是咱們有兩個動機去改寫一個方法,那麼這個方法就具備兩個職責。
  2. 原則
    一個對象(方法)只作一件事情。
  3. 設計模式驗證
    ① 代理模式
    圖片預加載代理模式中,代理對象負責預加載職責,本體對象負責圖片加載職責。
    ② 迭代器模式
    迭代器模式提供遍歷訪問聚合對象的職責。
    ③ 單例模式
    將建立對象與管理單例分別封裝在兩個方法中,兩個方法相互獨立互不影響。
    ④ 裝飾者模式
    讓類或者對象一開始只具備一些基礎的職責,更多的職責在代碼運行時被動態裝飾到對象上面。裝飾者模式能夠爲對象動態增長職責,從另外一個角度來看, 這也是分離職責的一種方式。
  4. 應用場景
    ① 若是有兩個職責老是同時變化,那就沒必要分離他們。即便兩個職責已經被耦合在一塊兒,但它們尚未發生改變的徵兆,那麼也許沒有必要主動分離它們。
    ② 在方便性與穩定性之間要有一些取捨。具體是選擇方便性仍是穩定性,並無標準答案,而是要取決於具體的應用環境。例如:jQueryattr 是個很是龐大的方法,既負責賦值,又負責取值,這對於jQuery的維護者來講,會帶來一些困難,但對於jQuery的用戶來講,卻簡化了用戶的使用。
  5. 優缺點
    ① 優勢
    按照職責把對象分解成更小的粒度,下降了單個類或者對象的複雜度,有助於代碼的複用,也有利於進行單元測試。
    ② 缺點
    增長了編寫代碼的複雜度,也增大了這些對象之間相互聯繫的難度。

3 最少知識原則

  1. 簡介
    最少知識原則(LKP)指一個軟件實體應當儘量少地與其餘實體發生相互做用。這裏的軟件實體不只包括對象,還包括系統、類、模塊、函數、變量等。
  2. 減小對象之間的聯繫
    單一職責原則指導咱們把對象劃分紅較小的粒度,提升對象的可複用性。但愈來愈多的對象之間可能會產生錯綜複雜的聯繫,若是修改了其中一個對象,極可能會影響到跟它相互引用的其餘對象。
    最少知識原則要求咱們儘可能減小對象之間的交互。若是兩個對象之間沒必要彼此直接通訊,那麼這兩個對象就不要發生直接的相互聯繫。
  3. 設計模式驗證
    ① 中介者模式
    增長一箇中介者對象,讓全部的相關對象都經過中介者對象來通訊,而不是互相引用。當一個對象發生改變時,只須要通知中介者對象便可。
    ② 外觀模式
    外觀模式對客戶提供一個簡單易用的高層接口,高層接口會把客戶的請求轉發給子系統來完成具體的功能實現。

4 開放-封閉原則

  1. 簡介
    軟件實體(類、模塊、函數)等應該是能夠擴展的,可是不可修改。
  2. 原則
    當須要改變一個程序的功能或者給這個程序增長新功能的時候,可使用增長代碼的方式,可是不容許改動程序的源代碼。
  3. 實現方式
    經過封裝變化的方式,能夠把系統中穩定不變的部分和容易變化的部分隔離開來。
    (1) 利用對象多態性
    利用對象的多態性來消除條件分支語句。
    (2) 放置掛鉤
    在程序有可能發生變化的地方放置一個掛鉤,掛鉤的返回結果決定了程序的下一步走向。
    (3) 回調函數
    把一部分易於變化的邏輯封裝在回調函數裏,而後把回調函數看成參數傳入一個穩定和封閉的函數中。
  4. 設計模式驗證
    ① 觀察者模式
    當有新的訂閱者出現時,發佈者的代碼不須要進行任何修改;一樣當發佈者須要改變時,也不會影響到以前的訂閱者。
    ② 模板方法模式
    子類的方法種類和執行順序都是不變的,因此咱們把這部分邏輯抽出來放到父類的模板方法裏面;而子類的方法具體怎麼實現則是可變的,因而把這部分變化的邏輯封裝到子類中。經過增長新的子類,便能給系統增長新的功能,並不須要改動抽象父類以及其餘的子類。
    ③ 策略模式
    策略模式將各類算法都封裝成單獨的策略類,這些策略類能夠被交換使用。策略和使用策略的客戶代碼能夠分別獨立進行修改而互不影響。
    ④ 代理模式
    圖片預加載示例中,代理函數proxyMyImage負責圖片預加載,myImage圖片加載函數不須要任何改動。
    ⑤ 職責鏈模式
    新增處理函數時,不須要改動原有的鏈條節點代碼,只須要在鏈條中增長一個新的節點。

5 代碼重構

  1. 提煉函數
    若是在函數中有一段代碼能夠被獨立出來,那咱們最好把這些代碼放進另一個獨立的函數中。
var getUserInfo = function () { ajax('http:// xxx.com/userInfo', function (data) { console.log('userId: ' + data.userId); console.log('userName: ' + data.userName); console.log('nickName: ' + data.nickName); }) } //改爲 var getUserInfo = function () { ajax('http:// xxx.com/userInfo', function (data) { printDetails(data); }); }; var printDetails = function (data) { console.log('userId: ' + data.userId); console.log('userName: ' + data.userName); console.log('nickName: ' + data.nickName); }; 
  1. 合併重複的條件片斷
    若是一個函數體內有一些條件分支語句,而這些條件分支語句內部散佈了一些重複的代碼,那麼就有必要進行合併去重工做。
var paging = function (currPage) { if (currPage <= 0) { currPage = 0; jump(currPage); // 跳轉 } else if (currPage >= totalPage) { currPage = totalPage; jump(currPage); } else { jump(currPage); } }; //改爲 var paging = function (currPage) { if (currPage <= 0) { currPage = 0; } else if (currPage >= totalPage) { currPage = totalPage; } jump(currPage); // 把 jump 函數獨立出來 }; 
  1. 把條件分支語句提煉成函數
    在程序設計中,複雜的條件分支語句是致使程序難以閱讀和理解的重要緣由,並且容易致使一個龐大的函數。
var getPrice = function (price) { var date = new Date(); if (date.getMonth() >= 6 && date.getMonth() <= 9) { return price * 0.8; } return price; }; //改爲 var isSummer = function () { var date = new Date(); return date.getMonth() >= 6 && date.getMonth() <= 9; }; var getPrice = function (price) { if (isSummer()) { // 夏天 return price * 0.8; } return price; }; 
  1. 合理使用循環
    在函數體內,若是有些代碼實際上負責的是一些重複性的工做,那麼合理利用循環不只能夠完成一樣的功能,還可使代碼量更少。
var createXHR = function () { var xhr; try { xhr = new ActiveXObject('MSXML2.XMLHttp.6.0'); } catch (e) { try { xhr = new ActiveXObject('MSXML2.XMLHttp.3.0'); } catch (e) { xhr = new ActiveXObject('MSXML2.XMLHttp'); } } return xhr; }; var xhr = createXHR(); //改爲 var createXHR = function () { var versions = ['MSXML2.XMLHttp.6.0ddd', 'MSXML2.XMLHttp.3.0', 'MSXML2.XMLHttp']; for (var i = 0, version; version = versions[i++];) { try { return new ActiveXObject(version); } catch (e) { } } }; var xhr = createXHR(); 
  1. 提早讓函數退出代替嵌套條件分支
    嵌套的條件分支語句絕對是代碼維護者的噩夢。嵌套的條件分支每每是由一些深信「每一個函數只能有一個出口的」程序員寫出的。但實際上,若是對函數的剩餘部分不感興趣,那就應該當即退出。
var del = function (obj) { var ret; if (!obj.isReadOnly) { // 不爲只讀的才能被刪除 if (obj.isFolder) { // 若是是文件夾 ret = deleteFolder(obj); } else if (obj.isFile) { ret = deleteFile(obj); } } return ret; }; var del = function (obj) { if (obj.isReadOnly) { return; } if (obj.isFolder) { return deleteFolder(obj); } if (obj.isFile) { return deleteFile(obj); } }; 
  1. 傳遞對象參數代替過長的參數列表
    有時候一個函數有可能接收多個參數,而參數的數量越多,函數就越難理解和使用。在使用的時候,還要當心翼翼,以避免少傳了某個參數或者把兩個參數搞反了位置。
    這時咱們能夠把參數都放入一個對象內,不用再關心參數的數量和順序,只要保證參數對應的 key 值不變就能夠了。
  2. 儘可能減小參數數量
    在實際開發中,向函數傳遞參數不可避免,但咱們應該儘可能減小函數接收的參數數量。
  3. 少用三目運算符
    若是條件分支邏輯簡單且清晰,這無礙咱們使用三目運算符;但若是條件分支邏輯很是複雜,咱們最好的選擇仍是循序漸進地編寫 if...else...
  4. 合理使用鏈式調用
    常用jQuery的程序員至關習慣鏈式調用方法,在JavaScript 中,能夠很容易地實現方法的鏈式調用,即讓方法調用結束後返回對象自身。
    使用鏈式調用的方式能夠省下一些字符和中間變量,但調試時不方便。若是咱們知道一條鏈中有錯誤出現,必須得先把這條鏈拆開才能加上一些調試log或者增長斷點。
    若是該鏈條的結構相對穩定,後期不易發生修改,可使用鏈式調用。若是該鏈條很容易發生變化,致使調試和維護困難,那麼仍是建議使用普通調用的形式。
  5. 分解大型類
    面向對象設計鼓勵將行爲分佈在合理數量的更小對象之中。
  6. return退出多重循環 假設在函數體內有一個兩重循環語句,咱們須要在內層循環中判斷,當達到某個臨界條件時退出外層的循環。咱們大多數時候會引入一個控制標記變量或設置循環標記。 這兩種作法無疑都讓人頭暈目眩,更簡單的作法是在須要停止循環的時候直接退出整個方法。 若是在循環以後還有一些將被執行的代碼,咱們能夠把循環後面的代碼放到 return 後面,若是代碼比較多,就應該把它們提煉成一個單獨的函數。
相關文章
相關標籤/搜索