问题背景:

页面中有多个功能模块,怎么在一个模块出了问题之后,保证其它模块的正常工作。

上面的差不多就是面试官的原话了,姑且称之为模块间错误隔离问题

第一反应是动态按需加载代码,用户操作发生后再加载对应模块代码,面试官(后文简称:对面)说所有模块代码都是在页面载入时加载的,不允许动态加载。

第二反应是error事件处理器return true,对面问确定这样做能隔离错误吗?不确定,好吧。。接着想

第三反应是try-catch,对面问怎么个try-catch法?说用try把各个模块包裹起来啊,也可以用工厂。。哦,那你写个工厂给我看看。。然后就傻傻地写了个这:

  1. function getModule(type){
  2. switch(type){
  3.  
  4. case Consts.type1 :
  5. return function(){
  6. try{
  7. Modules[type]();
  8. }catch(err){
  9. // fix
  10. }
  11. };
  12. break;
  13.  
  14. ...
  15. }
  16. }

看对面不是很满意,就又补充说也可以把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这样用比较好:
    1. function getSafeFun(fun){ // 集中处理错误
    2. return function(){
    3. try{
    4. fun();
    5. }catch(err){
    6. if (err instanceof TypeError) {
    7. // 类型不匹配
    8. }
    9. else if (err instanceof ReferenceError) {
    10. // 引用错误
    11. }
    12. else{
    13. // ...
    14. }
    15. }
    16. };
    17. }
    18.  
    19. function getSafeFun2(fun, errHandler){ // 针对函数处理错误
    20. return function(){
    21. try{
    22. fun();
    23. }catch(err){
    24. errHandler();
    25. }
    26. };
    27. }

    上面的是基础包装工具,还可以进一步封装,添一个好用的外观(Facade):

    1. // 配置数据
    2. var Modules = {};
    3. Modules.mod1 = {
    4. desc : "模块1",
    5. method : errorFun
    6. };
    7. Modules.mod2 = {
    8. desc : "模块2",
    9. method : fun
    10. };
    11.  
    12. /*
    13. * 统一模块调用接口
    14. */
    15. function use(moduleName, errHandler){
    16. if (typeof errHandler === "function") {
    17. getSafeFun2(Modules[moduleName].method, errHandler)();
    18. }
    19. else {
    20. getSafeFun(Modules[moduleName].method)();
    21. }
    22. }

    直接用use传入模块名和可选的错误处理器就可以隔离错误了,感觉好多了

    不需要工厂,工厂是根据给定的参数返回对应类型的东西,而我们所做的不过是用try包裹了一下而已,和工厂没多大关系,感觉和装饰、外观的关系更大一点。。当然,重要的是好用,而不是一定要用什么模式

二.测试验证

1.动态按需加载能不能隔离错误?

测试代码:

  1. <script type="text/javascript">
  2. script1
  3. alert(1);
  4. </script>
  5.  
  6. <script type="text/javascript">
  7. alert(2);
  8. </script>

运行结果:2,script标签能够隔离错误,所以动态加载也能隔离错误

2.error事件处理器return true能不能隔离错误?

测试代码:(在head里的script标签中插入如下代码)

  1. window.onerror = function(e){
  2. return true; // 不报错
  3. }

运行结果:不报错,也不会alert 1,对程序员而言没什么作用,不能隔离错误

3.闭包能不能隔离错误?如果把各个模块都放在各自的闭包里,像YUI一样,有用吗?

测试代码:

  1. // 闭包1
  2. (function(){
  3. closure
  4. alert(1);
  5. })();
  6. // 闭包2,无法执行,因为闭包1出错了
  7. (function(){
  8. alert(2);
  9. })();

运行结果:没有alert任何东西,只要闭包1和2在同一个script标签或者同一个外部js文件中,闭包2都会因为闭包1出错而无法执行,所以闭包不能隔离错误

4.try-catch怎么用才比较好?

当然不能强制要求所有编码人员都在调用模块的时候用try包裹,我们至少得有一个包装工具,像这样的:

  1. function getSafeFun(fun){ // 集中处理错误
  2. return function(){
  3. try{
  4. fun();
  5. }catch(err){
  6. if (err instanceof TypeError) {
  7. // 类型不匹配
  8. }
  9. else if (err instanceof ReferenceError) {
  10. // 引用错误
  11. }
  12. else{
  13. // ...
  14. }
  15. }
  16. };
  17. }
  18.  
  19. function getSafeFun2(fun, errHandler){ // 针对函数处理错误
  20. return function(){
  21. try{
  22. fun();
  23. }catch(err){
  24. errHandler();
  25. }
  26. };
  27. }
  28.  
  29. /* 测试 */
  30. function errorFun(){
  31. errorFunction
  32. alert(1);
  33. }
  34.  
  35. function fun(){
  36. alert(2);
  37. }
  38.  
  39. getSafeFun(errorFun)();
  40. getSafeFun(fun)();

现在有了getSafeFun()和getSafeFun2(),可以少写一点try了,但还是得要求所有编码人员自己看情况调用才能隔离错误,还是不科学,应该再添点什么

  1. // 配置数据
  2. var Modules = {};
  3. Modules.mod1 = {
  4. desc : "模块1",
  5. method : errorFun
  6. };
  7. Modules.mod2 = {
  8. desc : "模块2",
  9. method : fun
  10. };
  11.  
  12. /*
  13. * 统一模块调用接口
  14. */
  15. function use(moduleName, errHandler){
  16. if (typeof errHandler === "function") {
  17. getSafeFun2(Modules[moduleName].method, errHandler)();
  18. }
  19. else {
  20. getSafeFun(Modules[moduleName].method)();
  21. }
  22. }
  23.  
  24. /* 测试 */
  25. use("mod1");
  26. use("mod1", function(){
  27. alert("fix");
  28. });
  29. use("mod2");

现在就比较人性化了,只留一个入口,只需要告诉编码人员以前的模块调用方式过时了,现在的新API是use即可

三.结论

抛开问题本身,上面的所有测试结果可以归纳如下:

  1. 一个script标签中的代码发生错误,不会导致页面其它script标签内代码不执行
  2. window.onerror事件处理器中return true只能让浏览器不报错,而后面的代码不会再执行了
  3. 闭包对模块间错误隔离无益,但可以隔离模块间影响
  4. try-catch可以隔离错误,有错误隔离效果

参考资料

JS模块间错误隔离的更多相关文章

  1. js 页面间的通信

    看了一下公司原来的代码,原页面ajax post返回一个页面完整的HTML,然后再打开一个新页面并输出ajax返回的所有代码到新页面上,在新页面上以表单提交的形式实现重定向. 任凭我想了半天也没想出来 ...

  2. Node.js权威指南 (10) - Node.js中的错误处理与断言处理

    10.1 使用domain模块处理错误 / 272 10.1.1 domain模块概述 / 272 10.1.2 创建并使用Domain对象 / 274 10.1.3 隐式绑定与显式绑定 / 276 ...

  3. 如何发布一个自定义Node.js模块到NPM(详细步骤)

    咱们闲话不多说,直接开始! 由于我从没有使用过MAC,所以我不保证本文中介绍的操作与MAC一致. 文章开始我先假定各位已经在window全局安装了Node.js,下面开始进行详细步骤介绍: 本文本着, ...

  4. 【 js 模块加载 】深入学习模块化加载(node.js 模块源码)

    一.模块规范 说到模块化加载,就不得先说一说模块规范.模块规范是用来约束每个模块,让其必须按照一定的格式编写.AMD,CMD,CommonJS 是目前最常用的三种模块化书写规范.  1.AMD(Asy ...

  5. Node.js 模块

    稳定性: 5 - 锁定 Node 有简单的模块加载系统.在 Node 里,文件和模块是一一对应的.下面例子里,foo.js 加载同一个文件夹里的 circle.js 模块. foo.js 内容: va ...

  6. 【 js 模块加载 】【源码学习】深入学习模块化加载(node.js 模块源码)

    文章提纲: 第一部分:介绍模块规范及之间区别 第二部分:以 node.js 实现模块化规范 源码,深入学习. 一.模块规范 说到模块化加载,就不得先说一说模块规范.模块规范是用来约束每个模块,让其必须 ...

  7. 如何发布一个自定义Node.js模块到NPM(详细步骤,附Git使用方法)

    咱们闲话不多说,直接开始! 由于我从没有使用过MAC,所以我不保证本文中介绍的操作与MAC一致. 文章开始我先假定各位已经在window全局安装了Node.js,下面开始进行详细步骤介绍: 本文本着, ...

  8. 七、CommonJS规范和Note.js模块概念的介绍

    在计算机程序的开发过程中,随着程序代码越写越多,在一个文件里代码就会越来越长,越来越不容易维护.为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多 ...

  9. Developer - 如何自我保证Node.js模块质量

    组里正在做SaaS产品,其中一些模块(Module)是Node.js实现,这里我们主要使用Node.js实现Web Server来提供服务. 在做SaaS项目之前,组里的开发模式是传统的Deverlo ...

随机推荐

  1. Windows7 IE10运行不了JavaScript的问题

    如题,我的环境是Windows7 + IE10,JavaScript怎么也运行不了.郁闷了好一段时间. 后来发现一种办法终于可以让JavaScript运行起来. 具体:  点击 [工具] => ...

  2. Windows XP 32位系统安装MySQLdb

    环境 操作系统:Windows XP 32位 Python版本:2.7.11 安装 直接下载exe文件安装即可.

  3. TortoiseSVN-1.8.11 安装时弹出2503错误导致安装失败解决办法

    这个问题主要是由于msi格式文件在win8中默认不是以管理员身份运行造成,可通过命令行解决: 右键单击win8左下角启动图标,选择命令提示符(管理员): 输入:msiexec /package 要安装 ...

  4. UVa 10055 - Hashmat the Brave Warrior

    https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=94&page=s ...

  5. Mosquitto-Ubuntu 14.04快速安装问题解决

    Mosquitto是一个轻量级的MQTT Broker,支持很多种系统. 下载与安装:http://mosquitto.org/download/ 注意:由于客户端paho工程进展较快,目前需要使用最 ...

  6. Oracle 取随机数(转)

    1.从表中随机取记录 select * from (select * from staff order by dbms_random.random)      where rownum < 4 ...

  7. performSelector may cause a leak because its selector is unknown解决

    解决方法 SEL selector = NSSelectorFromString(@"applySketchFilter:"); IMP imp = [FWApplyFilter ...

  8. Docx读写Word

    Docx.dll功能比较强大,具备以下功能: 创建新的word文档或者读取已有的world文档 替换书签处内容: 插入表格或者在已有表格新增数据行: 插入图片,轻松设置图片大小: 保存或者另存为: 分 ...

  9. (引用) unittest测试驱动之执行测试(三)

    转载:http://www.wtoutiao.com/p/ydeoyY.html 在unittest的模块中,提供了TestRunner类来进行运行测试用例,在实际的应用中,经常使用的是TextTes ...

  10. static关键字用法

    java中static关键字可用于修饰: 1.属性:表示该属性变量在类被加载时即被创建并初始化,类加载过程只进行一次,因此静态变量也只被创建一次 2.方法:静态方法为类的公有方法,可直接用‘类名.方法 ...