【问题标题】:Wrapping a function in Javascript / jQuery在 Javascript / jQuery 中包装函数
【发布时间】:2011-03-10 11:09:12
【问题描述】:

如果我有一个任意函数myFunc,我的目标是用一个包装调用替换这个函数,该调用在它执行之前和之后运行代码,例如

// note: psuedo-javascript

var beforeExecute = function() { ... }
var afterExecute = function() { ... }

myFunc = wrap(myFunc, beforeExecute, afterExecute);

但是,我没有所需的wrap 函数的实现。像这样的jQuery中是否已经存在任何东西(我已经很好地浏览了文档但看不到任何东西)?或者,是否有人知道这是一个很好的实现,因为我怀疑如果我尝试自己编写它,我会错过很多边缘情况?

(顺便说一句 - 这样做的原因是对功能进行一些自动检测,因为我们在 Javascript 分析器等不可用的封闭设备上做了很多工作。如果有比这更好的方法,那么我会很感激答案也是如此。)

【问题讨论】:

    标签: javascript jquery instrumentation


    【解决方案1】:

    这是一个wrap 函数,它将使用完全相同的参数调用beforeafter 函数并且,如果提供了相同的this 值:

    var wrap = function (functionToWrap, before, after, thisObject) {
        return function () {
            var args = Array.prototype.slice.call(arguments),
                result;
            if (before) before.apply(thisObject || this, args);
            result = functionToWrap.apply(thisObject || this, args);
            if (after) after.apply(thisObject || this, args);
            return result;
        };
    };
    
    myFunc = wrap(myFunc, beforeExecute, afterExecute);
    

    【讨论】:

    • 我将勾选这个作为正确答案,因为我认为它是解决问题的最完整的解决方案,并且可以处理具有任意数量参数的函数。干杯!希望 jQuery 中可能有一个标准库函数,但我总是可以将它添加到我们的扩展中。
    • 我做了一个小修改,以便在创建包装器时没有指定 thisObject 时使用“常规”this 对象。
    【解决方案2】:

    接受的实现不提供有条件地调用包装(原始)函数的选项。

    这是包装和解包方法的更好方法:

      /*
        Replaces sMethodName method of oContext with a function which calls the wrapper 
        with it's list of parameters prepended by a reference to wrapped (original) function.
    
        This provides convenience of allowing conditional calls of the 
        original function  within the wrapper,
    
        unlike a common implementation that supplies "before" and "after" 
        cross cutting concerns as two separate methods.
    
        wrap() stores a reference to original (unwrapped) function for 
        subsequent unwrap() calls.
    
        Example: 
        =========================================
    
        var o = {
            test: function(sText) { return sText; }
        }
    
        wrap('test', o, function(fOriginal, sText) {
            return 'before ' + fOriginal(sText) + ' after';
        });
        o.test('mytext')  // returns: "before mytext after"
    
        unwrap('test', o);
        o.test('mytext')  // returns: "mytext"
    
        =========================================
    */
    function wrap(sMethodName, oContext, fWrapper, oWrapperContext) {
        var fOriginal = oContext[sMethodName];
    
        oContext[sMethodName] = function () {
            var a = Array.prototype.slice.call(arguments);
            a.unshift(fOriginal.bind(oContext));
            return fWrapper.apply(oWrapperContext || oContext, a);
        };
        oContext[sMethodName].unwrapped = fOriginal;
    };
    
    /*
        Reverts method sMethodName of oContext to reference original function, 
        the way it was before wrap() call
    */
    function unwrap(sMethodName, oContext) {
        if (typeof oContext[sMethodName] == 'function') {
            oContext[sMethodName] = oContext[sMethodName].unwrapped;
        }
    };
    

    【讨论】:

      【解决方案3】:

      这是我将使用的示例

      <script type="text/javascript">
        var before = function(){alert("before")};
        var after = function(param){alert(param)};
        var wrap = function(func, wrap_before, wrap_after){
          wrap_before.call();
          func.call();
          wrap_after.call();
        };
        wrap(function(){alert("in the middle");},before,function(){after("after")});
      </script>
      

      【讨论】:

      • 它还显示了如何参数化您正在使用 wrap 运行的函数。如果您像使用参数 whey 一样调用函数,则可能会在 wrap 之前执行。
      • 如果被包装的函数有参数,这似乎不起作用...?
      • 这就是为什么您应该将参数函数包装在 function(){...} 调用中。否则,您最终将触发该函数以获取其结果。
      【解决方案4】:

      你可以这样做:

      var wrap = function(func, pre, post)
      {
        return function()
        {
          var callee = arguments.callee;
          var args = arguments;
      
          pre();    
          func.apply(callee, args);    
          post();
        };
      };
      

      这将允许您这样做:

      var someFunc = function(arg1, arg2)
      {
          console.log(arg1);
          console.log(arg2);
      };
      
      someFunc = wrap(
          someFunc,
          function() { console.log("pre"); },
          function() { console.log("post"); });
      
      someFunc("Hello", 27);
      

      这给了我在 Firebug 中的输出:

      pre
      Hello
      27
      post
      

      以这种方式包装的重要部分是将您的参数从新函数传递回原始函数。

      【讨论】:

      • 为什么要将被调用者传递为this
      【解决方案5】:

      也许我错了,但我认为你可以直接创建一个匿名函数并将其分配给 myFunc:

      我的函数 = 函数(){ 前函数(); 我的函数(); 后功能(); }

      这样你可以控制每个函数的参数。

      【讨论】:

        猜你喜欢
        • 2012-02-02
        • 2010-09-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多