1. 程式人生 > >轉一個比較牛的JS Hook實現,基於Function prototype,能夠勾住和釋放任何函式

轉一個比較牛的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>