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能够隔离错误,有错误隔离效果

参考资料