views:

64

answers:

4

So, I would like to have some code executed everytime a given function is called, is that doable? Thing is, I could just overwrite the existing function and add the code, but that would require me to know the exact content of that function... Imagine a "plug-in" to a "library", and you want the plug-in to execute code everytime a specific function of this librairy is called, but you don't want to overwrite the original librairy content... Hope I'm clear enough!

And if it is doable, is there a way to retrieve the arguments submitted to the function?

+1  A: 

Possibly you could overwrite the reference to the function? For example:

function add1(x) { return x + 1; }
function log_calls(f, name) {
    return function() {
        console.log("got a call to", name, "with args", arguments);
        return f.apply(null, arguments);
    }
}
add1 = log_calls(add1, "add1");

Then calls to add1 would be something like:

>> x = add1(42)
"got call to add1 with args [42]"
>> x
43
David Wolever
Does what I need, thanks.
Alex
+3  A: 

Sounds like a job for a wrapper function to me.

Write a function that calls the other function, and put your code at the end of that (or wherever). Then always call your version instead.

Sort of an adapter pattern. http://en.wikipedia.org/wiki/Adapter_pattern

I believe you can hide the scope of the original function and name yours the same if you cannot modify calling code (doesn't work on some intrinsic functions like alert, but should work for library code). If that isn't an option, see if prototype will let you extend the object to add the functionality. http://phrogz.net/js/classes/ExtendingJavaScriptObjectsAndClasses.html

    //Only add this implementation if one does not already exist. 
if (Array.prototype.slice==null) Array.prototype.slice=function(start,end){ 
   if (start<0) start=this.length+start; //'this' refers to the object to which the prototype is applied 
   if (end==null) end=this.length;
   else if (end<0) end=this.length+end;
   var newArray=[];
   for (var ct=0,i=start;i<end;i++) newArray[ct++]=this[i];
   return newArray;
}

For arguments, you can also make your version take optional arguments (similar to jQuery / MooTools) and then look at what was passed.

Examples of optional arguments here. http://www.openjs.com/articles/optional_function_arguments.php

function accident() {
    for( var i = 0; i < arguments.length; i++ ) {
        alert("This accident was caused by " + arguments[i]);
    }
}
Nikki9696
Thanks for your answer! Will take me a bit reading all of that, but the arguments bit at the end of your post is really useful, never used that before ;). I didn't get what the array stuff had to do with my probelm though? Just a "demo" on how to use prototype?
Alex
Yes, it's copied from the link I posted just above it as part of the demo on prototype; they are adding a slice functionality to Array. I didn't want to copy their entire set of code as it's really long, but I didn't want to just leave a link here with no code either.
Nikki9696
Alright, thanks!
Alex
A: 

Edited to correct mistake pointed out in comment

This might solve your problem:

function f() {alert("in f");}
var saved_f = f;
var f = function() {
  alert("in f2");
  saved_f();
}
f();
Sid_M
`saved_f` will point to the last `f` defined function (causing infinite recursion). Function declarations are subject of [hoisting](http://stackoverflow.com/questions/1710424/referencing-a-javascript-value-before-it-is-declared-can-someone-explain-this/1710509#1710509). It can seem to work if you try it on the firebug's console, because Mozilla implementations define a Function statement, and the console evaluated code is wrapped in a `with` block. In fbug, try to wrap the above code in an anonymous function e.g. `(function (){})();`
CMS
@CMS Thanks for the correction and instruction. I've edited to take this into account (and tested outside firebug).
Sid_M
+1  A: 

It's definitely doable. JS is a dynamic language, so you can swap a function stored in an object with your own. For example:

var fxnToWrap = obj.someFunction;
obj.someFunction = function() {
    console.log("booyah, I'm intercepting every call to obj.someFunction");
    fxnToWrap.apply(obj, arguments);
}
kevingessner
That's exactly what I was looking for! Didn't know about .apply(), seems to be quite useful to me, thanks!
Alex
@kevingessner I'm pretty sure the next-to-last line should be fxnToWrap.apply(obj, arguments); since the first arg to apply sets the context in which the function runs (i.e. what will be assigned to this). By setting it to obj, the func will be able to use this to access the object's properties and methods.
Sid_M
@Sid_M Right you are! Fixed.
kevingessner