tags:

views:

944

answers:

4

I have a javascript function (class) that takes a function reference as one paremter.

function MyClass ( callBack ) {
  if (typeof callBack !== 'function')
    throw "You didn't pass me a function!"
}

For reasons I won't go in to here, I need to append something to the function by enclosing it in an anonymous function, but the only way I've been able to figure out how to do it is by adding a public function to MyClass that takes the callBack function as a parameter and returns the modified version.

function MyClass () {
    this.modifyCallBack = function ( callBack ) {
       var oldCallBack = callBack;
       callBack = function () {
           oldCallBack(); // call the original functionality
           /* new code goes here */
       }
       return callBack;
    }
}

/* elsewhere on the page, after the class is instantiated and the callback function defined */
myCallBackFunction = MyClassInstance.modifyCallBack( myCallBackFunction );

Is it possible to make this work when passing the callBack function as a parameter to the class? Attempting to modify the function in this manner when passign it as a parameter seems to only affect the instance of it in within the class, but that doesn't seem like it's a valid assumption since functions are Objects in javascript, and are hence passed by reference.

Update: as crescentfresh pointed out (and I failed to explain well), I want to modify the callBack function in-place. I'd rather not call a second function if it's possible to do all of this when the class is instantiated.

A: 

I assume you are saving this callback somewhere... Any reason this won't work?

function MyClass ( callBack ) {
  var myCallBack;

  if (typeof callBack !== 'function')
    throw "You didn't pass me a function!"

  var oldCallBack = callBack;
  callBack = function () {
    oldCallBack(); // call the original functionality
     /* new code goes here */
  }
  myCallBack = callback;
}
TJMonk15
since callBack is passed by reference, shouldn't assigning the anonymous function to it change it in the global namespace (where it was passed from)? Assigning it to myCallBack will create a new reference to it in the class, but the new code never gets run, making me thing I went wrong somewhere
dmercer
dmercer, you want to modify callBack *in place*? So that after passing it to your class the caller can call it but have it run some extra code?
Crescent Fresh
Yes, exactly. I normally wouldn't do something like this, but I'm somewhat constrained by the particular module I'm working with.
dmercer
A: 

You want to do something like:

function MyClass () {
    this.modifyCallBack = function ( callBack ) {
     var oldCallBack = callBack;
     callBack = function () {
      oldCallBack(); // call the original functionality
      alert("new functionality");
     }
     return callBack;
    }
}

/* elsewhere on the page, after the class is instantiated and the callback function defined */
var myCallBackFunction = function () {alert("original");};
var MyClassInstance = new MyClass();
myCallBackFunction = MyClassInstance.modifyCallBack( myCallBackFunction );
myCallBackFunction();
BenM
+2  A: 

Function objects don't provide methods to modify them. Therefore, what you want to do is impossible the way you want to do it. It's the same thing Jon Skeet likes to point out about Java: Objects are not really passed by reference, but instead a pointer to them is passed by value. That means that changing the value of an argument variable to a new one won't affect the original one at all.

There are only two ways to do what you want in call-by-value languages like Java and JavaScript: The first one would be to use the (function) object's methods to modify it. As I already stated, function objects don't have those. The other one is to pass the object of which the function object is a property as a second argument and set the appropriate property to a new function which wraps the old one.

Example:

var foo = {};
foo.func = function() {};

function wrapFunc(obj) {
    var oldFunc = obj.func;
    obj.func = function() {
        // do some stuff
        oldFunc.call(obj, _some_argument__);
    };
}

wrapFunc(foo);

This works for global functions as well: they are properties of the window object.

Christoph
Thanks for the detailed explanation. I wasn't understanding exactly how function Objects were passed.
dmercer
+1  A: 

As Javascript uses lexical scoping on variables the following is possible:

var modifiableCallback=function() { alert('A'); };


function ModifyCallbackClass(callback)
{
    modifiableCallback=function() { callback(); alert('B'); }; 
}


function body_onload()
{

    var myClass=new ModifyCallbackClass(modifiableCallback);

    modifiableCallback();

}

This does what you want, however the function "modifiableCallback" must be referred to with the same name inside ModifyCallbackClass, otherwise the closure will not be applied. So this may limit the usefulness of this approach for you a little.

Using eval (performance may suffer a bit) it is also possible to make this approach more flexible:

var modfiableCallback1=function() { alert('A'); };

var modfiableCallback2=function() { alert('B'); };

var modfiableCallback3=function() { alert('C'); };

function ModifyCallbackClass(callbackName)
{
    var temp=eval(callbackName);
    var temp2=eval(callbackName);

    temp= function() { temp2(); alert('Modified'); };

    eval(callbackName + " = temp;");
}


function body_onload()
{

    var myClass=new ModifyCallbackClass("modfiableCallback1");

    modfiableCallback1();

    myClass=new ModifyCallbackClass("modfiableCallback2");

    modfiableCallback2();

    myClass=new ModifyCallbackClass("modfiableCallback3");

    modfiableCallback3();

}
Ash