zoukankan      html  css  js  c++  java
  • Hook Javascript Function

    在公文升级方案中, 因为使用了新模板, 我需要在原来系统执行某几个函数之前, 做一些检查,如果成立则执行原有函数,否则执行我的新逻辑,然后再依情况决定是不是执行原函数。  

    我们知道,Javascript中函数有静态函数、成员函数和实例化对象的成员函数之分,这些函数的实现存在正常函数和匿名函数的区分。所以在我们Hook成员时,我们要对这些情况兼而顾之。

    例如对与下列函数Hook调用,预想产生如下的输出:

    //全局函数无参数

    function Method1(){

        alert('Method1');

    }

     

    Method1.hook(function()

        {

            alert('befor Method1');

            return true;

        },

        function()

        {

            alert('after Method1');

        }

    );

     

    Method1();

    /* 输出

    befor Method1

    Method1

    after Method1

    */

    alert('-----------------');

     

    //全局函数有参数

    function Method2(name){

        alert('Method2 with ' + name);

    }

    Method2.hook(function(name)

        {

            alert('befor Method2 with ' + name);

            return true;

        },

        function()

        {

            alert('after Method2 ');

        }

    );

    Method2('evlon');

    /* 输出

    befor Method2 with evlon

    Method2 with evlon

    after Method2

    */

     

    alert('-----------------');

     

    //Hook字符串的toString 函数

    String.prototype.toString.hook(String.prototype, function()

    {

        alert('befor string.toString');

        return true;

    });

     

    var s = "return s";

    alert(s.toString());

    /* 输出

    befor string.toString

    return s

    */

    alert('-----------------');

     

     

    //Hook 系统已有全局函数parseInt

    parseInt.hook(function()

    {

        alert('befor parseInt');

        return true;

    });

     

    alert(parseInt('5'));

    /* 输出

    befor parseInt

    5

    */

    alert('-----------------');

     

    //Hook 所有数组对象的join 方法

    Array.prototype.join.hook(Array.prototype, function(span)

    {

        alert('befor Array.prototype.join separator ' + span);

        return true;

    });

     

    alert([3,5,6].join(','));

    /* 输出

    befor Array.prototype.join separator ,

    3,5,6

    */

    alert('-----------------');

     

    var list = [3, 5, 6];

    //Hook list 这个实例数组对象的join 方法

    list.join.hook(list, function(span)

    {

        alert('befor list.join separator ' + span);

        return true;

    });

     

    alert(list.join(','));

    /* 输出

    befor list.join separator ,

    befor Array.prototype.join separator ,

    3,5,6

    */

    alert('-----------------');

     

     

    var list2 = [3, 5, 6, 7];

    // 此处调用不会产生befor list.join separator ,

    alert(list2.join(','));

     

    /* 输出

    befor Array.prototype.join separator ,

    3,5,6,7

    */

    alert('-----------------');

    //自定义类

    function People() {

    //成员函数带参数

    this.getName = function(name) {

    alert('in getName with ' + name);

    return 'return evlon';

    }

     

    }

     

    var p = new People();

    var p2 = new People();

     

    //这里我们只Hook实例p2 的函数调用

    p2.getName.hook(p2, 'getName2',

    function(name) {

    alert('befor getName2 with ' + name);

    return true;

    },

        function() {

    alert('after getName2');

        }

    );

    p2.getName.hook(p2,

    function(name) {

    alert('befor getName with ' + name);

    return true;

    },

        function() {

         alert('after getName');

        }

    );

     

    //因为只Hook了P2, 对这个调用无影响

    alert(p.getName('argName'));

    /* 输出

    in getName with argName

    return evlon

    */

    alert('-----------------');

     

    alert(p2.getName('argName'));

    /* 输出

    befor getName with argName

    in getName with argName

    after getName

    return evlon

    */

     

    alert('-----------------');

    alert(p2.getName2('argName2'));

    /* 输出

    befor getName2 with argName2

    in getName with argName2

    after getName2

    return evlon

    */

     

    要实现这样的东西,我们需要知道修改某一处的函数引用,对于这样的全局函数,我们知道它的所属对象是 window。如果对于类的成员函数,则应该是类.prototype.funName,对于实例的成员,我们可以通过在实例上添加函数来重写方法。  

    首先我们这个函数是所有函数的方法,所以它必须在 Function.prototyp 上添加一个新的函数Hook 它的逻辑应该是首先查找函数的名称,如果得到了,则直接改写在指定对象上的函数实现。但如果这个函数是匿名函数,我们需要查找这个对象的所有属性,看哪个属性和这个函数相等,如果有一个,则取出第一个相等的进行Hook。当然,如果Hook时明确指定了属性,则找到这个属性进行Hook

    下面是实现的代码:

    //这里我们改变输出方式

    window.alert = function(msg)

    {

        tbVal.innerText += '\n' + msg;

    }

    Function.prototype.hook = function(/*[context,]*//*[methodName,*/fnBefor /*[,fnAfter]*/) {

     

    //提出函数名称

    function getFunName(fn) {

    var method = fn.toString();

    var rgx = /function\s+(\w+)\s*\(/;

    var r = method.match(rgx);

    if (r) {

    return r[1];

    }

     

    return '';

    }

     

    //加载参数

    var context = null;

    var methodName = null;

     

    var argIndex = 0;

    if (typeof (arguments[argIndex]) != 'function' && typeof (arguments[argIndex]) != 'string')

    context = arguments[argIndex++];

     

    if (typeof (arguments[argIndex]) == 'string')

    methodName = arguments[argIndex++];

     

    fnBefor = arguments[argIndex++] || function() { }

    var fnAfter = arguments[argIndex++] || function() { }

     

    //处理默认参数

    context = context || window;

    var method = this;

     

    var methodName = methodName || getFunName(method);

    if (methodName == '') {

    //找不到,可能是匿名函数,我们从对象中查找试试

    var bFound = false;

    var contextObject = context.constructor == Array.prototype.constructor ? Array.prototype : context;

    for (var m in contextObject) {

    //    alert(m + ':' + context[m]);

    if (context[m] == method) {

    methodName = m;

    bFound = true;

    break;

    }

    }

    if (!bFound) {

    alert('请提供正确的函数所属对象使用有名函数');

    return null;

    }

    }

     

    //生成有参数名的新函数

    eval('context[methodName] = function ' + methodName + '()\n' +

        '{\n' +

        '    var args = Array.prototype.slice.call(arguments,0);\n' +

        '    var ctx = this;\n' +

        '    try\n' +

        '    {\n' +

        '        if(fnBefor.apply(ctx, args))\n' +

        '            return method.apply(ctx, args);\n' +

        '    }\n' +

        '    finally\n' +

        '    {\n' +

        '        fnAfter.apply(ctx, args);\n' +

        '    }\n' +

        '};');

    }

    不过写完了,发现不能Unhook一个函数,下次改进吧,写了一天了这个东西,得赶赶进度了。

  • 相关阅读:
    jQuery源码学习9——DOMReady加载
    jQuery源码学习8——工具方法之init
    jQuery源码学习7——实例成员
    jQuery源码学习6——工具方法之事件系统
    SQL中EXCEPT函数在 Mysql 和 sqlServer 中的替代方法
    关系型数据库及优势
    jsp小基础归纳
    eclipse换了高版本的maven插件后报错:org.apache.maven.archiver.MavenArchiver.getManifest(org.apache.maven.project
    开发常用网站收藏
    Struts2
  • 原文地址:https://www.cnblogs.com/evlon/p/1624860.html
Copyright © 2011-2022 走看看