Automatisches Hinzufügen von console.log zu jeder Funktion

97

Gibt es eine Möglichkeit, eine Funktion dazu zu bringen, eine console.log-Anweisung auszugeben, wenn sie aufgerufen wird, indem irgendwo ein globaler Hook registriert wird (dh ohne die eigentliche Funktion selbst zu ändern) oder auf andere Weise?

JRL
quelle
Ausgezeichnete Frage, ich würde gerne wissen, ob dies möglich ist, aber ich bin mir ziemlich sicher, dass dies nicht der Fall ist. Vielleicht fügen Sie eine Funktionsanforderung hinzu, damit sie in der js-Engine für Ihren Lieblingsbrowser hinzugefügt wird. :-)
Endophage
Perfekte Frage, ich brauche so etwas Ähnliches
mjimcua

Antworten:

62

Hier ist eine Möglichkeit, alle Funktionen im globalen Namespace mit der Funktion Ihrer Wahl zu erweitern:

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

Ein Nachteil ist, dass keine nach dem Aufruf erstellten Funktionen augmentdas zusätzliche Verhalten aufweisen.

Wayne
quelle
Behandelt es Rückgabewerte der Funktion richtig?
SunnyShah
2
@SunnyShah Nein, das tut es nicht: jsfiddle.net/Shawn/WnJQ5 Aber das tut es: jsfiddle.net/Shawn/WnJQ5/1, obwohl ich nicht sicher bin, ob es in ALLEN Fällen funktionieren wird ... Der Unterschied ändert sich fn.apply(this, arguments);zureturn fn.apply(this, arguments);
Shawn
2
@Shawn @SunnyShah Behoben. Muss nur zur returninnersten Funktion hinzugefügt werden.
Wayne
funktioniert fast gut, aber ich bekomme einen Fehler mit dieser jquery: call: if (jQuery.isFunction (lSrc)) und es heißt: TypeError: jQuery.isFunction ist keine Funktion
fekiri malek
4
Diese Lösung verwendet nicht jQuery
Wayne
8

Für mich scheint dies die eleganteste Lösung zu sein:

(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);
    };
}());
Sergey Mell
quelle
7

Proxy-Methode zum Protokollieren von Funktionsaufrufen

Es gibt eine neue Möglichkeit, Proxy zu verwenden, um diese Funktionalität in JS zu erreichen. Nehmen wir an, wir möchten eine haben, console.logwenn eine Funktion einer bestimmten Klasse aufgerufen wird:

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

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

Wir können unsere Klasseninstanziierung durch einen Proxy ersetzen, der jede Eigenschaft dieser Klasse überschreibt. so:

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"

Überprüfen Sie, ob dies in Codepen tatsächlich funktioniert


Denken ProxySie daran, dass die Verwendung viel mehr Funktionen bietet, als nur Konsolennamen zu protokollieren.

Auch diese Methode funktioniert in Node.js auch.

Soorena
quelle
Können Sie dies auch ohne Verwendung von Instanzen und Klassen tun? Speziell in node.js?
Revadike
@Revadike dies sollte helfen: stackoverflow.com/a/28708700/5284370
Soorena
1

Wenn Sie eine gezieltere Protokollierung wünschen, protokolliert der folgende Code Funktionsaufrufe für ein bestimmtes Objekt. Sie können sogar Objektprototypen so ändern, dass auch alle neuen Instanzen protokolliert werden. Ich habe Object.getOwnPropertyNames anstelle von for ... in verwendet, daher funktioniert es mit ECMAScript 6-Klassen, die keine aufzählbaren Methoden haben.

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);
Peter Tseng
quelle
-1

Hier ist etwas Javascript, das das Hinzufügen von console.log zu jeder Funktion in Javascript ersetzt. Spielen Sie damit auf Regex101 :

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

Es ist ein "schneller und schmutziger Hack", aber ich finde ihn nützlich zum Debuggen. Wenn Sie viele Funktionen haben, achten Sie darauf, dass dadurch viel Code hinzugefügt wird. Außerdem ist RegEx einfach und funktioniert möglicherweise nicht für komplexere Funktionsnamen / Deklarationen.

Hexikel
quelle
-10

Sie können Ihre eigene Funktion für alles, was geladen wird, an console.log anhängen.

console.log = function(msg) {
    // Add whatever you want here
    alert(msg); 
}
Henrik Albrechtsson
quelle
2
richtig oder nicht, es beantwortet die Frage nicht. überhaupt. (falls jemand noch verwirrt war)
redfox05