頁面中有多個功能模塊,怎麼在一個模塊出了問題以後,保證其它模塊的正常工做。javascript
上面的差很少就是面試官的原話了,姑且稱之爲模塊間錯誤隔離問題java
第一反應是動態按需加載代碼,用戶操做發生後再加載對應模塊代碼,面試官(後文簡稱:對面)說全部模塊代碼都是在頁面載入時加載的,不容許動態加載。程序員
第二反應是error事件處理器return true,對面問肯定這樣作能隔離錯誤嗎?不肯定,好吧。。接着想面試
第三反應是try-catch,對面問怎麼個try-catch法?說用try把各個模塊包裹起來啊,也能夠用工廠。。哦,那你寫個工廠給我看看。。而後就傻傻地寫了個這:設計模式
function getModule(type){ switch(type){ case Consts.type1 : return function(){ try{ Modules[type](); }catch(err){ // fix } }; break; ... } }
看對面不是很滿意,就又補充說也能夠把case裏的匿名方法提出來,做爲一個包裝工具,但就只能作統一錯誤處理,而用這個能夠針對模塊作不一樣的錯誤處理,各有各的好處。。。對面勉強點頭瀏覽器
以後對面沉默了好久,2分鐘吧,有些忐忑,就弱弱地問是否是上個問題的答案不是您想要的?對面說:還行,用異常處理包起來確實能夠。。閉包
但感受和對面想要的答案仍是有些差距,因此有了本文函數
從上面的面試過程能找出幾個子問題:工具
先給出測試結果:學習
function getSafeFun(fun){ // 集中處理錯誤 return function(){ try{ fun(); }catch(err){ if (err instanceof TypeError) { // 類型不匹配 } else if (err instanceof ReferenceError) { // 引用錯誤 } else{ // ... } } }; } function getSafeFun2(fun, errHandler){ // 針對函數處理錯誤 return function(){ try{ fun(); }catch(err){ errHandler(); } }; }
上面的是基礎包裝工具,還能夠進一步封裝,添一個好用的外觀(Facade):
// 配置數據 var Modules = {}; Modules.mod1 = { desc : "模塊1", method : errorFun }; Modules.mod2 = { desc : "模塊2", method : fun }; /* * 統一模塊調用接口 */ function use(moduleName, errHandler){ if (typeof errHandler === "function") { getSafeFun2(Modules[moduleName].method, errHandler)(); } else { getSafeFun(Modules[moduleName].method)(); } }
直接用use傳入模塊名和可選的錯誤處理器就能夠隔離錯誤了,感受好多了
不須要工廠,工廠是根據給定的參數返回對應類型的東西,而咱們所作的不過是用try包裹了一下而已,和工廠沒多大關係,感受和裝飾、外觀的關係更大一點。。固然,重要的是好用,而不是必定要用什麼模式
測試代碼:
<script type="text/javascript"> script1 alert(1); </script> <script type="text/javascript"> alert(2); </script>
運行結果:2,script標籤可以隔離錯誤,因此動態加載也能隔離錯誤
測試代碼:(在head裏的script標籤中插入以下代碼)
window.onerror = function(e){ return true; // 不報錯 }
運行結果:不報錯,也不會alert 1,對程序員而言沒什麼做用,不能隔離錯誤
測試代碼:
// 閉包1 (function(){ closure alert(1); })(); // 閉包2,沒法執行,由於閉包1出錯了 (function(){ alert(2); })();
運行結果:沒有alert任何東西,只要閉包1和2在同一個script標籤或者同一個外部js文件中,閉包2都會由於閉包1出錯而沒法執行,因此閉包不能隔離錯誤
固然不能強制要求全部編碼人員都在調用模塊的時候用try包裹,咱們至少得有一個包裝工具,像這樣的:
function getSafeFun(fun){ // 集中處理錯誤 return function(){ try{ fun(); }catch(err){ if (err instanceof TypeError) { // 類型不匹配 } else if (err instanceof ReferenceError) { // 引用錯誤 } else{ // ... } } }; } function getSafeFun2(fun, errHandler){ // 針對函數處理錯誤 return function(){ try{ fun(); }catch(err){ errHandler(); } }; } /* 測試 */ function errorFun(){ errorFunction alert(1); } function fun(){ alert(2); } getSafeFun(errorFun)(); getSafeFun(fun)();
如今有了getSafeFun()和getSafeFun2(),能夠少寫一點try了,但仍是得要求全部編碼人員本身看狀況調用才能隔離錯誤,仍是不科學,應該再添點什麼
// 配置數據 var Modules = {}; Modules.mod1 = { desc : "模塊1", method : errorFun }; Modules.mod2 = { desc : "模塊2", method : fun }; /* * 統一模塊調用接口 */ function use(moduleName, errHandler){ if (typeof errHandler === "function") { getSafeFun2(Modules[moduleName].method, errHandler)(); } else { getSafeFun(Modules[moduleName].method)(); } } /* 測試 */ use("mod1"); use("mod1", function(){ alert("fix"); }); use("mod2");
如今就比較人性化了,只留一個入口,只須要告訴編碼人員之前的模塊調用方式過期了,如今的新API是use便可
拋開問題自己,上面的全部測試結果能夠概括以下: