Can I override the behavior of the Function object so that I can inject behavior prior t every function call, and then carry on as normal? Specifically, (though the general idea is intriguing in itself) can I log to the console every function call without having to insert console.log statements everywhere? And then the normal behavior goes on?
I do recognize that this will likely have significant performance problems; I have no intention of having this run typically, even in my development environment. But if it works it seems an elegant solution to get a 1000 meter view on the running code. And I suspect that the answer will show me something deeper about javascript.
JavaScript supports overriding not overloading, meaning, that if you define two functions with the same name, the last one defined will override the previously defined version and every time a call will be made to the function, the last defined one will get executed.
log() is a function in JavaScript which is used to print any kind of variables defined before in it or to just print any message that needs to be displayed to the user. Syntax: console. log(A);
The JavaScript call() Method The call() method is a predefined JavaScript method. It can be used to invoke (call) a method with an owner object as an argument (parameter). With call() , an object can use a method belonging to another object.
In order to run a function multiple times after a fixed amount of time, we are using few functions. setInterval() Method: This method calls a function at specified intervals(in ms). This method will call continuously the function until clearInterval() is run, or the window is closed.
The obvious answer is something like the following:
var origCall = Function.prototype.call; Function.prototype.call = function (thisArg) { console.log("calling a function"); var args = Array.prototype.slice.call(arguments, 1); origCall.apply(thisArg, args); };
But this actually immediately enters an infinite loop, because the very act of calling console.log
executes a function call, which calls console.log
, which executes a function call, which calls console.log
, which...
Point being, I'm not sure this is possible.
Many here have tried to override .call. Some have failed, some have succeeded. I'm responding to this old question, as it has been brought up at my workplace, with this post being used as reference.
There are only two function-call related functions available for us to modify: .call and .apply. I will demonstrate a successful override of both.
TL;DR: What OP is asking is not possible. Some of the success-reports in the answers are due to the console calling .call internally right before evaluation, not because of the call we want to intercept.
This appears to be the first idea people come up with. Some have been more successful than others, but here is an implementation that works:
// 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); };
This successfully intercepts Function.prototype.call
Lets take it for a spin, shall we?
// Some tests console.log("1"); // Does not show up console.log.apply(console,["2"]); // Does not show up console.log.call(console, "3"); // BINGO!
It is important that this is not run from a console. The various browsers have all sorts of console tools that call .call themselves a lot, including once for every input, which might confuse a user in the moment. Another mistake is to just console.log arguments, which goes through the console api for stringification, which in turn cause an infinite loop.
Well, what about apply then? They're the only magic calling functions we have, so lets try that as well. Here goes a version that catches both:
// 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); }
... And lets try it out!
// Some tests console.log("1"); // Passes by unseen console.log.apply(console,["2"]); // Caught console.log.call(console, "3"); // Caught
As you can see, the calling parenthesis go unnoticed.
Fortunately, calling parenthesis cannot be intercepted from JavaScript. But even if .call would intercept the parenthesis operator on function objects, how would we call the original without causing an infinite loop?
The only thing overriding .call/.apply does is to intercept explicit calls to those prototype functions. If the console is used with that hack in place, there will be lots and lots of spam. One must furthermore be very careful if it is used, as using the console API can quickly cause an infinite loop (console.log will use .call internally if one gives it an non-string).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With