转一个比较牛的JS Hook实现,基于Function prototype,能够勾住和释放任何函数

转自

基于原型的hook实现:

[bool]hook:params{
    realFunc[String|must]:用于保存原始函数的函数名称,用于unHook;
    hookFunc[Function|must]:替换的hook函数;
    context[Object|opt]:目标函数所在对象,用于hook非window对象下的函数,如String.protype.slice,carInstance1
    methodName[String|opt]:匿名函数需显式传入目标函数名eg:this.Begin = function(){....};
}
[bool]unhook:params{
    realFunc[String|must]:用于保存原始函数的函数名称,用于unHook;
    funcName[String|must]:被Hook的函数名称
    context[Object|opt]:目标函数所在对象,用于hook非window对象下的函数,如String.protype.slice,carInstance1
}
<!DOCTYPE html>
<html>
    <head>
        <title>
        </title>
        <script type="text/javascript">
        function Hooks(){
        return {
            initEnv:function () {
                Function.prototype.hook = function (realFunc,hookFunc,context,funcName) {
                    var _context = null; //函数上下文
                    var _funcName = null; //函数名

                    _context = context || window;
                    _funcName = funcName || getFuncName(this);
                    _context[realFunc] = this;

                    if(_context[_funcName].prototype && _context[_funcName].prototype.isHooked){
                        console.log("Already has been hooked,unhook first");
                        return false;
                    }
                    function getFuncName (fn) {
                        // 获取函数名
                        var strFunc = fn.toString();
                        var _regex = /function\s+(\w+)\s*\(/;
                        var patten = strFunc.match(_regex);
                        if (patten) {
                            return patten[1];
                        };
                        return '';
                    }
                    try{
                        eval('_context[_funcName] = function '+_funcName+'(){\n'+
                            'var args = Array.prototype.slice.call(arguments,0);\n'+
                            'var obj = this;\n'+
                            'hookFunc.apply(obj,args)\n'+
                            'return _context[realFunc].apply(obj,args);\n'+
                            '};');
                        _context[_funcName].prototype.isHooked = true;
                        return true;
                    }catch (e){
                        console.log("Hook failed,check the params.");
                        return false;
                    }
                }
                Function.prototype.unhook = function (realFunc,funcName,context) {
                    var _context = null;
                    var _funcName = null;
                    _context = context || window;
                    _funcName = funcName;
                    if (!_context[_funcName].prototype.isHooked)
                    {
                        console.log("No function is hooked on");
                        return false;
                    }
                    _context[_funcName] = _context[realFunc];
                    delete _context[realFunc];
                    return true;
                }
            },
            cleanEnv:function () {
                if(Function.prototype.hasOwnProperty("hook")){
                    delete Function.prototype.hook;
                }
                if(Function.prototype.hasOwnProperty("unhook")){
                    delete Function.prototype.unhook;
                }
                return true;
            }
        };
    }

    var hook = Hooks();
    hook.initEnv();

    // 这个是要执行的正常的函数
    function test(){
        alert('test');
    }

    // 这个是钩子函数。此钩子函数内心戏:
    // 我只喜欢test函数,所以我必须出现在她前面(在她前面执行),这样她才能看到我。
    function hookFunc(){
        alert('hookFunc');
    }

    // hookFunc钩住test
    test.hook(test,hookFunc,window,"test");

    window.onload = function(){
      // 由于钩子函数hookFunc钩住了test函数,所以test执行时,会先执行hookFunc。
      test();
    }
    </script>
    </head>
    <body>
    </body>

</html>

猜你喜欢

转载自blog.csdn.net/flyfeifei66/article/details/84197149
今日推荐