页面中有多个功能模块,怎么在一个模块出了问题以后,保证其它模块的正常工做。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便可
抛开问题自己,上面的全部测试结果能够概括以下: