views:

95

answers:

2

Hi,

Problem & Reason

One of my team mate ended up in messy situtaion implementing function hooking in javascript. this is the actual code

 function ActualMethod(){   
       this.doSomething = function() {
              this.testMethod();
        };

       this.testMethod = function(){
            alert("testMethod");
        };
   }


 function ClosureTest(){
       var objActual= new ActualMethod();   
    var closeHandler = objActual.doSomething;   
    closeHandler();     
    closeHandler.apply(objActual,arguments); //the fix i have added
    this.ActualTest = function() {
        alert("ActualTest");
        };
  }

In the above code, var closeHandler is created in the context of ClosureTest(), but it holds the handler of the ActualMethod.doSomething. Whenever calling the closeHandler() ended up in "object doesnt support this method" error.

This is because doSomething() function calls another method inside called this.testMethod();. Here this refers to the context of the caller not callee.so i assume the closeHandler is bound to the environment(ClosureTest) actually created.Even though it holds the handler to the another context, it just exposes the properties of its own context.

Solution

To avoid this i suggest to use apply to specify the conext in which it needs to execute.

closeHandler.apply(objActual,arguments);

Questions

is it perfect scenario for closures..??

What are the intersting places you have encountered closures in javascript..?

UPDATE

Yes its simple i can call the method directly. but the problem is, in a particular scenario I need to intercept the call to actuall method and run some code before that, finally execute the actual method..

say for an example, am using 3rd party aspx grid library, and all the mouseclick events are trapped by their controls. In particular group by mouse click i need to intercept the call to their ilbrary method and hook my mthod to execute instead and redirect the call to actual library method

hope this helps

Cheers

RameshVel

+2  A: 

Update: Because you probably left out some details in your code, it is difficult to adapt it into something workable without missing the point of your actual code. I do think I understand your underlying problem as you describe it. I hope the following helps.

Suppose the following simple example:

// Constructor function.
function Example() {
    // Method:
    this.method = function() {
        alert("original method");
    }
}

// You would use it like this:
var obj = new Example();
obj.method(); // Calls original method.

To intercept such a method call, you can do this:

function wrap(obj) {
    var originalMethod = obj.method;
    obj.method = function() {
        alert("intercepted call");
        originalMethod.apply(this, arguments);
    }
    return obj;
}

var obj = wrap(new Example());
obj.method(); // Calls wrapped method.

Unfortunately, because method() is defined in the constructor function, not on a prototype, you need to have an object instance to wrap the object.


Answer to original question: The doSomething() function is used as a method on objects created with ActualMethod(). You should use it as a method, not detach it and use it as a function in a different context. Why don't you just call the method directly?

function ClosureTest(){
    var objActual = new ActualMethod();
    // Call method directly, avoid messy apply() calls.
    objActual.doSomething();
    this.ActualTest = function() {
        alert("ActualTest");
    };
}

If you assign a method (a function on some object) to a local variable in Javascript and call it, the context will be different (the value of this changes). If you don't want it to happen, don't do it.

molf
Hi molf.. in a particular scenario I need to intercept the call to particular method and run some code before that, and finally execute the actual method.. thats the reason behind that...
Ramesh Vel
@molf, check out my updated question. you will get a overall picture of the problem
Ramesh Vel
thanks for the update...
Ramesh Vel
+2  A: 

When I want to hook a function, I use the following Function method which is also a fine piece of Closure demonstration:

Function.prototype.wrap = function (wrapper) {
 var __method = this;
 return function() {
  var __obj = this;
  var args = [ __method.bind(__obj) ];
  for(var i=0; i<arguments.length; i++) args.push(arguments[i]);
  return wrapper.apply(__obj, args);
 }
};

Then do something like:

ActualMethod = ActualMethod.wrap(function (proceed, option) {
  // ... handle option
  proceed(); // calls the wrapped function
});

proceed is bound to its initial object, so you can safely call it.

Alsciende
@Alsciende, thanks for the idea.. i ll try this...
Ramesh Vel