web-dev-qa-db-fra.com

Ajout automatique de console.log à chaque fonction

Existe-t-il un moyen de faire en sorte qu'une fonction génère une instruction console.log lorsqu'elle est appelée en enregistrant un hook global quelque part (c'est-à-dire sans modifier la fonction réelle elle-même) ou via un autre moyen?

90
JRL

Voici un moyen d'augmenter toutes les fonctions de l'espace de noms global avec la fonction de votre choix:

function augment(withFn) {
    var name, fn;
    for (name in window) {
        fn = window[name];
        if (typeof fn === 'function') {
            window[name] = (function(name, fn) {
                var args = arguments;
                return function() {
                    withFn.apply(this, args);
                    return fn.apply(this, arguments);

                }
            })(name, fn);
        }
    }
}

augment(function(name, fn) {
    console.log("calling " + name);
});

Un inconvénient est qu'aucune fonction créée après avoir appelé augment n'aura le comportement supplémentaire.

60
Wayne Burkett

Méthode proxy pour consigner les appels de fonction

Il existe une nouvelle façon d'utiliser Proxy pour obtenir cette fonctionnalité dans JS. supposons que nous voulons avoir un console.log chaque fois qu'une fonction d'une classe spécifique est appelée:

class TestClass {
  a() {
    this.aa = 1;
  }
  b() {
    this.bb = 1;
  }
}

const foo = new TestClass()
foo.a() // nothing get logged

nous pouvons remplacer notre instanciation de classe par un proxy qui remplace chaque propriété de cette classe. alors:

class TestClass {
  a() {
    this.aa = 1;
  }
  b() {
    this.bb = 1;
  }
}


const logger = className => {
  return new Proxy(new className(), {
    get: function(target, name, receiver) {
      if (!target.hasOwnProperty(name)) {
        if (typeof target[name] === "function") {
          console.log(
            "Calling Method : ",
            name,
            "|| on : ",
            target.constructor.name
          );
        }
        return new Proxy(target[name], this);
      }
      return Reflect.get(target, name, receiver);
    }
  });
};



const instance = logger(TestClass)

instance.a() // output: "Calling Method : a || on : TestClass"

vérifiez que cela fonctionne réellement dans Codepen


N'oubliez pas que l'utilisation de Proxy vous offre beaucoup plus de fonctionnalités que la simple journalisation des noms de console.

Cette méthode fonctionne également dans Node.js également.

6
Soorena

Si vous souhaitez une journalisation plus ciblée, le code suivant enregistrera les appels de fonction pour un objet particulier. Vous pouvez même modifier les prototypes d'objets afin que toutes les nouvelles instances soient également enregistrées. J'ai utilisé Object.getOwnPropertyNames au lieu de for ... in, donc cela fonctionne avec les classes ECMAScript 6, qui n'ont pas de méthodes énumérables.

function inject(obj, beforeFn) {
    for (let propName of Object.getOwnPropertyNames(obj)) {
        let prop = obj[propName];
        if (Object.prototype.toString.call(prop) === '[object Function]') {
            obj[propName] = (function(fnName) {
                return function() {
                    beforeFn.call(this, fnName, arguments);
                    return prop.apply(this, arguments);
                }
            })(propName);
        }
    }
}

function logFnCall(name, args) {
    let s = name + '(';
    for (let i = 0; i < args.length; i++) {
        if (i > 0)
            s += ', ';
        s += String(args[i]);
    }
    s += ')';
    console.log(s);
}

inject(Foo.prototype, logFnCall);
1
Peter Tseng

Voici du Javascript qui remplace ajoute console.log à chaque fonction en Javascript; Jouez avec sur Regex101 :

$re = "/function (.+)\\(.*\\)\\s*\\{/m"; 
$str = "function example(){}"; 
$subst = "$& console.log(\"$1()\");"; 
$result = preg_replace($re, $subst, $str);

C'est un "hack rapide et sale" mais je le trouve utile pour le débogage. Si vous avez beaucoup de fonctions, méfiez-vous car cela ajoutera beaucoup de code. De plus, le RegEx est simple et peut ne pas fonctionner pour des noms/déclarations de fonctions plus complexes.

0
hexicle

Pour moi, cela ressemble à la solution la plus élégante:

(function() {
    var call = Function.prototype.call;
    Function.prototype.call = function() {
        console.log(this, arguments); // Here you can do whatever actions you want
        return call.apply(this, arguments);
    };
}());
0
Sergey Mell