Intercepter les appels de fonction
Beaucoup ici ont essayé de passer outre l'appel .call. Certains ont échoué, d'autres ont réussi. Je réponds à cette vieille question, car elle a été soulevée sur mon lieu de travail, ce message étant utilisé comme référence.
Il n'y a que deux fonctions liées à l'appel de fonction que nous pouvons modifier : .call et .apply. Je vais vous montrer comment modifier ces deux fonctions avec succès.
TL;DR : Ce que le PO demande n'est pas possible. Certains des rapports de réussite dans les réponses sont dus au fait que la console appelle .call en interne juste avant l'évaluation, et non à l'appel que nous voulons intercepter.
Remplacement de Function.prototype.call
Il semble que ce soit la première idée qui vienne aux gens. Certaines ont eu plus de succès que d'autres, mais voici une mise en œuvre qui fonctionne :
// Store the original
var origCall = Function.prototype.call;
Function.prototype.call = function () {
// If console.log is allowed to stringify by itself, it will
// call .call 9 gajillion times. Therefore, lets do it by ourselves.
console.log("Calling",
Function.prototype.toString.apply(this, []),
"with:",
Array.prototype.slice.apply(arguments, [1]).toString()
);
// A trace, for fun
console.trace.apply(console, []);
// The call. Apply is the only way we can pass all arguments, so don't touch that!
origCall.apply(this, arguments);
};
Cela intercepte avec succès Function.prototype.call.
Faisons un tour, d'accord ?
// Some tests
console.log("1"); // Does not show up
console.log.apply(console,["2"]); // Does not show up
console.log.call(console, "3"); // BINGO!
Il est important que cette opération ne soit pas exécutée à partir d'une console. Les différents navigateurs disposent de toutes sortes d'outils de console qui appellent .call eux-mêmes beaucoup y compris une fois pour chaque entrée, ce qui pourrait déconcerter l'utilisateur sur le moment. Une autre erreur consiste à n'utiliser que les arguments console.log, qui passent par l'API de la console pour la stringification, ce qui entraîne une boucle infinie.
Remplacer Function.prototype.apply également
Alors, pourquoi ne pas s'appliquer ? Ce sont les seules fonctions d'appel magique que nous ayons, alors essayons-les aussi. Voici une version qui prend les deux :
// Store apply and call
var origApply = Function.prototype.apply;
var origCall = Function.prototype.call;
// We need to be able to apply the original functions, so we need
// to restore the apply locally on both, including the apply itself.
origApply.apply = origApply;
origCall.apply = origApply;
// Some utility functions we want to work
Function.prototype.toString.apply = origApply;
Array.prototype.slice.apply = origApply;
console.trace.apply = origApply;
function logCall(t, a) {
// If console.log is allowed to stringify by itself, it will
// call .call 9 gajillion times. Therefore, do it ourselves.
console.log("Calling",
Function.prototype.toString.apply(t, []),
"with:",
Array.prototype.slice.apply(a, [1]).toString()
);
console.trace.apply(console, []);
}
Function.prototype.call = function () {
logCall(this, arguments);
origCall.apply(this, arguments);
};
Function.prototype.apply = function () {
logCall(this, arguments);
origApply.apply(this, arguments);
}
... Et essayons-le !
// Some tests
console.log("1"); // Passes by unseen
console.log.apply(console,["2"]); // Caught
console.log.call(console, "3"); // Caught
Comme vous pouvez le constater, les parenthèses d'appel passent inaperçues.
Conclusion
Heureusement, les parenthèses d'appel ne peuvent pas être interceptées depuis JavaScript. Mais même si .call interceptait l'opérateur de parenthèse sur les objets fonctionnels, comment pourrions-nous appeler l'original sans provoquer une boucle infinie ?
La seule chose que fait le remplacement de .call/.apply est d'intercepter les appels explicites à ces fonctions prototypes. Si la console est utilisée avec cette modification, il y aura beaucoup de spam. Il faut en outre être très prudent si on l'utilise, car l'utilisation de l'API de la console peut rapidement provoquer une boucle infinie (console.log utilisera .call en interne si on lui donne une chaîne autre qu'une chaîne).