Domanda

Sto scrivendo un errore globale nella gestione del "modulo" per una delle mie applicazioni.

Una delle funzionalità che voglio avere è quella di essere in grado di avvolgere facilmente una funzione con un blocco try {} catch {} , in modo che tutte le chiamate a quella funzione abbiano automaticamente la gestione degli errori codice che chiamerà il mio metodo di registrazione globale. (Per evitare di inquinare il codice ovunque con i blocchi try / catch).

Questo è, tuttavia, leggermente al di là della mia comprensione del funzionamento di basso livello di JavaScript, i metodi .call e .apply e il this parola chiave.

Ho scritto questo codice, basato sul metodo Function.wrap di Prototype:

Object.extend(Function.prototype, {
  TryCatchWrap: function() {
    var __method = this;
    return function() {
            try { __method.apply(this, arguments) } catch(ex) { ErrorHandler.Exception(ex); }
    }
  }
});

Che viene utilizzato in questo modo:

function DoSomething(a, b, c, d) {
    document.write(a + b + c)
    alert(1/e);
}

var fn2 = DoSomething.TryCatchWrap();
fn2(1, 2, 3, 4);

Quel codice funziona perfettamente. Stampa 6 e quindi chiama il mio gestore di errori globale.

La mia domanda è: questo romperà qualcosa quando la funzione che sto avvolgendo è all'interno di un oggetto, e usa il "questo" operatore? Sono un po 'preoccupato dal momento che sto chiamando .apply, passando qualcosa lì, temo che questo possa rompere qualcosa.

È stato utile?

Soluzione

Personalmente invece di inquinare gli oggetti incorporati andrei con una tecnica di decorazione:

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

Puoi usarlo così:

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);

Ma se hai voglia di modificare i prototipi, puoi scriverlo in questo modo:

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);
    }
  };
};

Il miglioramento evidente sarà la parametrizzazione di makeSafe () in modo da poter specificare quale funzione chiamare nel blocco catch.

Altri suggerimenti

Risposta 2017 : basta usare ES6. Data la seguente funzione demo:

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

Puoi far funzionare il tuo wrapper senza bisogno di librerie esterne:

var wrap = function(someFunction){
  var wrappedFunction = function(){
    var args = [...arguments].splice(0)
    console.log(`You're about to run a function with these arguments: \n     ${args}`)
    return someFunction(args)
  }
  return wrappedFunction
}

In uso:

doThing = wrap(doThing)

doThing('one', {two:'two'}, 3)

risposta 2016 : utilizza il modulo wrap :

Nell'esempio seguente sto avvolgendo process.exit () , ma funziona perfettamente con qualsiasi altra funzione (incluso anche il browser JS).

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);

Object.extend (Function.prototype, { Object.extend nella console di Google Chrome mi dà "non definito" Bene, ecco un esempio funzionante:

    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 ) );

così invece di Object.extend (Function.prototype, {...}) Fallo come: Function.prototype.extend = {}

Avvolgimento delle funzioni alla vecchia maniera:

//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);

Lo stesso in stile ES6:

//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);

Per quanto riguarda l'inquinamento degli spazi dei nomi, in realtà li inquinerò ancora un po '... Poiché tutto ciò che accade in JS è iniziato da un evento di qualche tipo, sto pianificando di chiamare la mia funzione di wrapper magico dal metodo Prototype Event.observe (), quindi non ho bisogno di chiamarlo ovunque.

Vedo gli svantaggi di tutto questo, ovviamente, ma questo particolare progetto è comunque fortemente legato a Prototype e voglio che questo codice del gestore degli errori sia il più globale possibile, quindi non è un grosso problema.

Grazie per la tua risposta!

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top