JS模塊間錯誤隔離

問題背景:

頁面中有多個功能模塊,怎麼在一個模塊出了問題以後,保證其它模塊的正常工做。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分鐘吧,有些忐忑,就弱弱地問是否是上個問題的答案不是您想要的?對面說:還行,用異常處理包起來確實能夠。。閉包

但感受和對面想要的答案仍是有些差距,因此有了本文函數

一.子問題

從上面的面試過程能找出幾個子問題:工具

  1. 動態按需加載能不能隔離錯誤?
  2. error事件處理器return true能不能隔離錯誤?
  3. 閉包能不能隔離錯誤?若是把各個模塊都放在各自的閉包裏,像YUI同樣,有用嗎?
  4. try-catch怎麼用才比較好?必定要用工廠嗎?

先給出測試結果:學習

  1. 動態加載能隔離錯誤,由於在沒有try-catch的狀況下,錯誤的影響範圍(做用域?)是script標籤或者整個外部js文件,也就是說,若是script標籤中或者外部js文件的第n行發生了錯誤,那麼第n行後面的代碼都不會再執行了。。。因此經過插入script標籤來動態加載,確實能隔離錯誤
  2. 處理error事件不能隔離錯誤,讓error事件處理器返回true只能抑制瀏覽器報錯,沒有什麼恢復斷點的做用,對程序員而言並無實際意義
  3. 閉包不能隔離錯誤,但能夠隔離影響,YUI的每一個模塊都被放在閉包中,這樣能夠更方便地管理做用域,避免模塊間的相互影響
  4. try-catch這樣用比較好:
    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包裹了一下而已,和工廠沒多大關係,感受和裝飾、外觀的關係更大一點。。固然,重要的是好用,而不是必定要用什麼模式

二.測試驗證

1.動態按需加載能不能隔離錯誤?

測試代碼:

<script type="text/javascript">
    script1
    alert(1);
</script>

<script type="text/javascript">
    alert(2);
</script>

運行結果:2,script標籤可以隔離錯誤,因此動態加載也能隔離錯誤

2.error事件處理器return true能不能隔離錯誤?

測試代碼:(在head裏的script標籤中插入以下代碼)

window.onerror = function(e){
    return true;    // 不報錯
}

運行結果:不報錯,也不會alert 1,對程序員而言沒什麼做用,不能隔離錯誤

3.閉包能不能隔離錯誤?若是把各個模塊都放在各自的閉包裏,像YUI同樣,有用嗎?

測試代碼:

// 閉包1
(function(){
    closure
    alert(1);
})();
// 閉包2,沒法執行,由於閉包1出錯了
(function(){
    alert(2);
})();

運行結果:沒有alert任何東西,只要閉包1和2在同一個script標籤或者同一個外部js文件中,閉包2都會由於閉包1出錯而沒法執行,因此閉包不能隔離錯誤

4.try-catch怎麼用才比較好?

 固然不能強制要求全部編碼人員都在調用模塊的時候用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便可

三.結論

拋開問題自己,上面的全部測試結果能夠概括以下:

  1. 一個script標籤中的代碼發生錯誤,不會致使頁面其它script標籤內代碼不執行
  2. window.onerror事件處理器中return true只能讓瀏覽器不報錯,然後面的代碼不會再執行了
  3. 閉包對模塊間錯誤隔離無益,但能夠隔離模塊間影響
  4. try-catch能夠隔離錯誤,有錯誤隔離效果

參考資料

相關文章
相關標籤/搜索