1

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?

Alex
  • 1,157
  • 3
  • 11
  • 25
  • In [aspect oriented](http://en.wikipedia.org/wiki/Aspect-oriented_programming) and [functional](http://en.wikipedia.org/wiki/Functional_programming) programming, what you're asking about is called ["advice"](http://en.wikipedia.org/wiki/Advice_%28programming%29). – outis Oct 28 '10 at 00:06

4 Answers4

3

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
  • 6,260
  • 1
  • 28
  • 23
  • 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 Oct 27 '10 at 21:13
  • 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 Oct 27 '10 at 22:16
1

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
  • 148,955
  • 89
  • 346
  • 502
1

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
  • 18,559
  • 5
  • 43
  • 63
  • That's exactly what I was looking for! Didn't know about .apply(), seems to be quite useful to me, thanks! – Alex Oct 27 '10 at 21:01
  • 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 Oct 27 '10 at 23:43
0

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
  • 399
  • 1
  • 4
  • `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 (){})();` – Christian C. Salvadó Oct 27 '10 at 20:47
  • @CMS Thanks for the correction and instruction. I've edited to take this into account (and tested outside firebug). – Sid_M Oct 27 '10 at 23:52