How do I wrap a function in Javascript?

前端 未结 6 1706
南笙
南笙 2020-12-12 16:48

I\'m writing a global error handling \"module\" for one of my applications.

One of the features I want to have is to be able to easily wrap a function with a t

相关标签:
6条回答
  • 2020-12-12 17:04

    Object.extend(Function.prototype, { Object.extend in the Google Chrome Console gives me 'undefined' Well here's some working example:

        Boolean.prototype.XOR =
    //  ^- Note that it's a captial 'B' and so
    //      you'll work on the Class and not the >b<oolean object
            function( bool2 ) { 
    
               var bool1 = this.valueOf();
               //         'this' refers to the actual object - and not to 'XOR'
    
               return (bool1 == true   &&   bool2 == false)
                   || (bool1 == false   &&   bool2 == true);
            } 
    
    alert ( "true.XOR( false ) => " true.XOR( false ) );
    

    so instead of Object.extend(Function.prototype, {...}) Do it like: Function.prototype.extend = {}

    0 讨论(0)
  • 2020-12-12 17:08

    2017 answer: just use ES6. Given the following demo function:

    function doThing(){
      console.log(...arguments)
    }
    

    You can make your own wrapper function without needing external libraries:

    
    function wrap(someFunction){
      function wrappedFunction(){
        var newArguments = [...arguments]
        newArguments.push('SECRET EXTRA ARG ADDED BY WRAPPER!')
        console.log(`You're about to run a function with these arguments: \n  ${newArguments}`)
        return someFunction(...newArguments)
      }
      return wrappedFunction
    }
    

    In use:

    doThing('one', 'two', 'three')
    

    Works as normal.

    But using the new wrapped function:

    const wrappedDoThing = wrap(doThing)
    wrappedDoThing('one', 'two', 'three')
    

    Returns:

    one two three SECRET EXTRA ARG ADDED BY WRAPPER!
    

    2016 answer: use the wrap module:

    In the example below I'm wrapping process.exit(), but this works happily with any other function (including browser JS too).

    var wrap = require('lodash.wrap');
    
    var log = console.log.bind(console)
    
    var RESTART_FLUSH_DELAY = 3 * 1000
    
    process.exit = wrap(process.exit, function(originalFunction) {
        log('Waiting', RESTART_FLUSH_DELAY, 'for buffers to flush before restarting')
        setTimeout(originalFunction, RESTART_FLUSH_DELAY)
    });
    
    process.exit(1);
    
    0 讨论(0)
  • 2020-12-12 17:08

    As far as polluting the namespaces, I'm actually going to pollute them some more... Since everything that happens in JS is initiated by an event of some kind, I'm planning to call my magical wrapper function from within the Prototype Event.observe() method, so I don't need to call it everywhere.

    I do see the downsides of all this, of course, but this particular project is heavily tied to Prototype anyway, and I do want to have this error handler code be as global as possible, so it's not a big deal.

    Thanks for your answer!

    0 讨论(0)
  • 2020-12-12 17:12

    Personally instead of polluting builtin objects I would go with a decorator technique:

    var makeSafe = function(fn){
      return function(){
        try{
          return fn.apply(this, arguments);
        }catch(ex){
          ErrorHandler.Exception(ex);
        }
      };
    };
    

    You can use it like that:

    function fnOriginal(a){
      console.log(1/a);
    };
    
    var fn2 = makeSafe(fnOriginal);
    fn2(1);
    fn2(0);
    fn2("abracadabra!");
    
    var obj = {
      method1: function(x){ /* do something */ },
      method2: function(x){ /* do something */ }
    };
    
    obj.safeMethod1 = makeSafe(obj.method1);
    obj.method1(42);     // the original method
    obj.safeMethod1(42); // the "safe" method
    
    // let's override a method completely
    obj.method2 = makeSafe(obj.method2);
    

    But if you do feel like modifying prototypes, you can write it like that:

    Function.prototype.TryCatchWrap = function(){
      var fn = this; // because we call it on the function itself
      // let's copy the rest from makeSafe()
      return function(){
        try{
          return fn.apply(this, arguments);
        }catch(ex){
          ErrorHandler.Exception(ex);
        }
      };
    };
    

    Obvious improvement will be to parameterize makeSafe() so you can specify what function to call in the catch block.

    0 讨论(0)
  • 2020-12-12 17:12

    The following wrapping utility takes a function and enables the developer to inject a code or wrap the original:

    
    function wrap(originalFunction, { inject, wrapper } = {}) {
    
        const wrapperFn = function(...args) {
            if (typeof inject === 'function') {
                inject(originalFunction, this);
            }
            if (typeof wrapper === 'function') {
                return wrapper(originalFunction, this, args);
            }
            return originalFunction.apply(this, args);
        };
    
        // copy the original function's props onto the wrapper
        for(const prop in originalFunction) {
          if (originalFunction.hasOwnProperty(prop)) {
            wrapperFn[prop] = originalFunction[prop];
          }
        }
        return wrapperFn;
    }
    

    Usage example:

    
    // create window.a()
    (function() {
    
        const txt = 'correctly'; // outer scope variable
        
        window.a = function a(someText) { // our target
            if (someText === "isn't") {
                throw('omg');
            }
            return ['a', someText, window.a.c, txt].join(' ');
        };
        
        window.a.c = 'called'; // a.c property example
    })();
    
    const originalFunc = window.a;
    console.log(originalFunc('is')); // logs "a is called correctly"
    
    window.a = wrap(originalFunc);
    console.log(a('is')); // logs "a is called correctly"
    
    window.a = wrap(originalFunc, { inject(func, thisArg) { console.log('injected function'); }});
    console.log(a('is')); // logs "injected function\na is called correctly"
    
    window.a = wrap(originalFunc, { wrapper(func, thisArg, args) { console.log(`doing something else instead of ${func.name}(${args.join(', ')})`); }});
    console.log(a('is')); // logs "doing something else instead of a(is)"
    
    window.a = wrap(originalFunc, {
        wrapper(func, thisArg, args) {
            try {
                return func.apply(thisArg, args);
            } catch(err) {
                console.error('got an exception');
            }
        }
    });
    a("isn't"); // error message: "got an exception"
    

    The last example demonstrates how to wrap your function with a try-catch clause

    0 讨论(0)
  • 2020-12-12 17:23

    Function wrapping in good old fashion:

    //Our function
    function myFunction() {
      //For example we do this:
      document.getElementById('demo').innerHTML = Date();
      return;
    }
    
    //Our wrapper - middleware
    function wrapper(fn) {
      try {
        return function(){
          console.info('We add something else', Date());
          return fn();
        }
      }
      catch (error) {
        console.info('The error: ', error);
      }
    }
    
    //We use wrapper - middleware
    myFunction = wrapper(myFunction);
    

    The same in ES6 style:

    //Our function
    let myFunction = () => {
      //For example we do this:
      document.getElementById('demo').innerHTML = Date();
      return;
    }
    
    //Our wrapper - middleware
    const wrapper = func => {
      try {
        return () => {
          console.info('We add something else', Date());
          return func();
        }
      }
      catch (error) {
        console.info('The error: ', error);
      }
    }
    
    //We use wrapper - middleware
    myFunction = wrapper(myFunction);
    
    0 讨论(0)
提交回复
热议问题