web-dev-qa-db-fra.com

Comment envelopper une fonction en Javascript?

J'écris un "module" global de gestion des erreurs pour l'une de mes applications.

Une des fonctionnalités que je souhaite avoir est de pouvoir encapsuler facilement une fonction avec un try{} catch{} block, de sorte que tous les appels à cette fonction auront automatiquement le code de gestion des erreurs qui appellera ma méthode de journalisation globale. (Pour éviter de polluer le code partout avec des blocs try/catch).

Cela dépasse cependant légèrement ma compréhension du fonctionnement de bas niveau de JavaScript, le .call et .apply et le mot clé this.

J'ai écrit ce code, basé sur le Function.wrap méthode:

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

Qui est utilisé comme ceci:

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

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

Ce code fonctionne parfaitement. Il imprime 6, puis appelle mon gestionnaire d'erreur global.

Ma question est la suivante: est-ce que cela va casser quelque chose lorsque la fonction que j'encapsule est dans un objet et qu'elle utilise l'opérateur "this"? Je suis un peu inquiet depuis que j'appelle .apply, en passant quelque chose là-bas, j'ai peur que cela puisse casser quelque chose.

29
Daniel Magliola

Personnellement, au lieu d'objets intégrés polluants, j'opterais pour une technique de décoration:

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

Vous pouvez l'utiliser comme ça:

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

Mais si vous avez envie de modifier des prototypes, vous pouvez l'écrire comme ça:

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

Une amélioration évidente sera de paramétrer makeSafe () afin que vous puissiez spécifier la fonction à appeler dans le bloc catch.

48
Eugene Lazutkin

réponse 2017: utilisez simplement ES6. Compte tenu de la fonction de démonstration suivante:

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

Vous pouvez créer votre propre fonction wrapper sans avoir besoin de bibliothèques externes:

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
}

Utilisé:

doThing = wrap(doThing)

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

réponse 2016: utilisez le module wrap:

Dans l'exemple ci-dessous, j'emballe process.exit(), mais cela fonctionne parfaitement avec n'importe quelle autre fonction (y compris le navigateur JS aussi).

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

Object.extend (Function.prototype, {Object.extend dans la console Google Chrome Console me donne 'indéfini' ') Eh bien, voici un exemple de travail:

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

donc au lieu de Object.extend (Function.prototype, {...}) Faites-le comme: Function.prototype.extend = {}

2
DigitalDemenz

Fonction d'emballage à l'ancienne:

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

Idem dans le style 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);
0
Roman