移动端tryjs异常捕获

上周处理了一下群活动的badjs,第一步是摆脱Script error.,捕获异常栈,找到自己是错在哪里~ 分享一下这个步骤

异步的切入点:

1、XMLHttpRequest.prototype.send

2、setTimeout、setInterval

3、define、require

4、Zepto的事件绑定 on 、bind (另外要能off、unbind)

何时引入切入文件:

最简单的方法是在requirejs引入后立刻做define,require的切入, 在Zepto加载之后做on,bind的切入。或者在zepto引入之后做所有的切入。但这样无法对inline进去的代码做切入,比如预加载的代码。既然是移动端,使用window.__defineSetter__去监听define,zepto的出现也许是最好的选择~

下面是主要的代码:

    _.before(XMLHttpRequest.prototype, 'send', function() {                         
        if (this.onreadystatechange) {                                              
            this.onreadystatechange = cat(this.onreadystatechange);                 
        }                                                                           
    });                                                                             

    _.before(window, 'setTimeout setInterval', catArgs);                            

    // 打包后define require有时候会被封在函数里,可以手动暴露到window上             
    _.modify(window, 'define require', funArgsFilter(catArgs));                     

    _.modify(window, 'Jquery Zepto', function($) {                                  
        if ($ && $.fn) {                                                            
            _.modify($.fn, 'on bind', funArgsFilter(catArgs));                      
            _.modify($.fn, 'off unbind', funArgsFilter(uncatArgs));                 
        }                                                                           
        return $;                                                                   
    });                                                                             

    // 手动接口                                                                     
    window.cat = cat;

需要注意:

1、对$.fn.bind做切入,一定也要对$.fn.unbind做切入,使unbind能工作

2、切入函数和被切入函数尽量保持一对一,要不然容易出现未知的问题

3、define、require在打包之后不一定会暴露到window对象上来,可以手动暴露一下

完整代码:

(function() {
    var _ = {
        isFunction: function(fun) {
            return typeof fun === 'function';
        },
        // aop 注入
        before: function(obj, props, hook) {
            props.split(/\s+/).forEach(function(prop) {
                var _fun = obj[prop];
                obj[prop] = function() {
                    var args = hook.apply(this, arguments) || arguments;
                    return _fun.apply(this, args);
                };
            });
        },
        // 监听修改属性
        modify: function(obj, props, modifier) {
            if (!obj.__defineSetter__) {
                return;
            }
            props.split(/\s+/).forEach(function(prop) {
                var value = obj[prop];
                // 如果属性已经存在
                if (typeof value !== 'undefined') {
                    value = modifier.call(this, value);
                }
                obj.__defineSetter__(prop, function(_value) {
                    if (_value !== value) {
                        value = modifier.call(this, _value);
                    }
                });
                obj.__defineGetter__(prop, function() {
                    return value;
                });
            });
        }
    };


    var onthrow = function(e) {
        console.log(e);
        // TODO report
        // 设置一个标志位, window.onerror跳过这个异常,随后设为false
        window._errorthrowing = true;
        throw e;
    };


    /**
     * 包装函数
     */
    function cat(foo) {
        // 防止多次包装
        if (!_.isFunction(foo) || foo.__try) {
            return foo;
        }
        // 保持一对一,要不然容易引起未知的问题
        // 例如: 两次ele.bind('', fun)再ele.unbind('')会有一个无法unbind
        if (foo.__tryer) {
            return foo.__tryer;
        }
        var fun = function() {
            try {
                return foo.apply(this, arguments);
            } catch (e) {
                onthrow(e);
            }
        };
        foo.__tryer = fun;
        fun.__try = foo;
        fun.__proto__ = foo;
        return fun;
    }

    /**
     * 包装参数中的函数
     */
    function catArgs() {
        return [].slice.call(arguments).map(function(fun) {
            return _.isFunction(fun) ? cat(fun) : fun;
        });
    }

    /**
     * 反包装参数中的函数
     */
    function uncatArgs() {
        return [].slice.call(arguments).map(function(fun) {
            return _.isFunction(fun) && fun.__tryer ? fun.__tryer : fun;
        });
    }

    function funArgsFilter(filter) {
        return function(_fun) {
            if (!_.isFunction(_fun) || _fun.__filting) {
                return _fun;
            }
            if (_fun.__filter) {
                return _fun.__filter;
            }
            var fun = function() {
                var args = filter.apply(this, arguments);
                return _fun.apply(this, args);
            };
            _fun.__filter = fun;
            fun.__filting = _fun;
            fun.__proto__ = _fun;
            return fun;
        };
    }

    _.before(XMLHttpRequest.prototype, 'send', function() {
        if (this.onreadystatechange) {
            this.onreadystatechange = cat(this.onreadystatechange);
        }
    });

    _.before(window, 'setTimeout setInterval', catArgs);

    // 打包后define require有时候会被封在函数里,可以手动暴露到window上
    _.modify(window, 'define require', funArgsFilter(catArgs));

    _.modify(window, 'Jquery Zepto', function($) {
        if ($ && $.fn) {
            _.modify($.fn, 'on bind', funArgsFilter(catArgs));
            _.modify($.fn, 'off unbind', funArgsFilter(uncatArgs));
        }
        return $;
    });

    // 手动接口
    window.cat = cat;
})();

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏walterlv - 吕毅的博客

为带有多种语言的 Jekyll 博客添加多语言选择

发布于 2018-03-06 06:47 更新于 2018-09...

1061
来自专栏云瓣

通过一个demo了解Redux

TodoList小demo 效果展示 项目地址 (单向)数据流 数据流是我们的行为与响应的抽象;使用数据流能帮我们明确了行为对应的响应,这和react的状态可预...

31510
来自专栏张善友的专栏

Expression Web设计工具

从MS company store买了个Expression web软件,在Vista下安装了一个,今后可以用这个工具和Vistual studio 2005协...

18710
来自专栏企鹅号快讯

Python实现一个带图形界面的爬虫

Python 是一个很简介高效的语言,许多的 Python 爱好者(包括我在内)都很喜欢用 Python 做爬虫,为什么会喜欢写爬虫,我对爬虫的感觉就是:它能给...

4725
来自专栏小狼的世界

学习使用YUI3

对YUI一直很有好感,最近开始看了看YUI CSS GRID,发现这个理念非常好,非常有利于我们工作效率的提高,特别是熟悉了这套CSS之后,我们就不必每一个项目...

772
来自专栏全沾开发(huā)

函数节流与函数防抖

函数节流与函数防抖 函数节流和函数防抖是一个老生常谈的话题了,两者都是对大量频繁重复调用代码的一种优化方案 今天在某群和大家讨论时,顺便搜了一些相关博客 发现...

4226
来自专栏张善友的专栏

Web打印组件jatoolsPrinter

应用web化,不论对开发商,还是对用户来说,实在是一种很经济的选择,因为基于web的应用,客户端的规则很简单,容易学习,容易维护,容易发布。但对程序员来说,因为...

4459
来自专栏技术博文

关于微信二次分享,标题变链接的解决方法(二)----代码部分

声明: 本篇博文只是个人工作中的分享总结,仅代表个人观点,虽然解决了不少网友的问题,但同时也引来了一些网友的不满,所以特此声明,当您遇到本博文解决不了的问题,可...

3486
来自专栏大史住在大前端

【Recorder.js+百度语音识别】全栈方案技术细节

技术栈:React+recorder-tool.js +recorder.js + Express + Baidu语音识别API

2993
来自专栏jeremy的技术点滴

免编译在JSP中直接写react代码

4485

扫码关注云+社区