Tutorial — Advanced Techniques
Part 10 of a Guide to Function Interception in JavaScript
Contents
Using call and apply
Normally, a call to a method causes the JavaScript interpreter to set the 'this' reference to point to the object of which the method is a member. This means, for example, that calls to global functions set the 'this' reference to point to the Global object (the 'window' object in client-side JavaScript).
Similarly, calls to a non-global function, such as:
 MyObj.Func_A ();
...Will cause the interpreter to set the 'this' reference to point to MyObj. In the case of constructor calls, the interpreter sets the 'this' reference to point to the object that is created by the action of the 'new' operator.
However, JavaScript functions are objects in the 'class' sense, and provide two methods called 'call' and 'apply'. These (very similar) functions allow a method's caller to specify explicitly what the 'this' reference should point to. For example:
 MyObj.Func_A.call (YourObj);
...Will cause Func_A to execute as if it were a member of YourObj (the 'this' reference will refer to YourObj).
AspectJS is completely transparent from the viewpoint of interceptees and their callers, therefore the 'apply' and 'call' methods operate as usual. In Example Fifty-Eight, a prefix is applied to MyFunc, which is then invoked using the call method of the underlying function-object, wherein the 'this' reference is set to point at YourObj. At this point the prefix executes, followed by MyFunc, which now has access to YourObj's member (and methods, if it had any).
Consult a good JavaScript tutorial/reference for more information on the call and apply methods of function objects.
Note that most AspectJS methods cannot be invoked using 'apply' or 'call', and where the 'this' reference is set to point at an object other than AJS or AJS_HP. Attempts to do so will raise an exception because it would not be useful to allow users to invoke AspectJS in the context of another object; moreover, such invocations could corrupt the interception mechanism associated with a given interceptee.
Note also that if any object's method is invoked using call or apply, and the 'this' reference is set to point to the true owner of the AspectJS method then this is the same as a normal call to the object's method, in which case the use of call or apply is redundant.

 // Example 58

 var YourObj =
    {
    Name : "Barney"
    };

 function Prefix () { alert ("Prefix executed"); }

 function MyFunc () { alert ("MyFunc executed - Name = " + this.Name); }


 AJS.AddPrefix (this, "MyFunc", Prefix);

 MyFunc.call (YourObj);

 --------------------------------------

 Output:

 Prefix executed
 MyFunc executed - Name = Barney
            
Inner Functions
The ability to use inner functions as prefixes or suffixes depends on the browser in question. One would presume it to be possible because functions are 'class-like' objects, just like any other type in JavaScript, and have methods such as call and apply. Given this premise, one might surmise that an inner function is therefore a method of the function within which it is defined, and can therefore be pressed into service as an affix.
However, this argument is suspect because functions in JavaScript have scope, therefore an inner function should not be visible from outside its definer, and should therefore not be available as a prefix or suffix.
Technicalities aside, however, Example Fifty-Nine illustrates the idea, and (surprisingly) works in Firefox 1.5, but does not work in Internet Explorer 6.

 // Example 59

 function YourFunc () { alert ("YourFunc executed"); }

 function MyFunc ()
    {
    function MyInner ()
       {
       alert ("MyInner executed");
       }

    }

 AJS.AddPrefix (this, "YourFunc", MyFunc.MyInner);

 YourFunc ();

 --------------------------------------

 Output (in Firefox):

 MyInner executed
 YourFunc executed
            
Certainly, the reverse situation, wherein an affix is applied to an inner function (either from inside or outside its definer) cannot work because the following expression:
(typeof IntercepteeOwner === 'object')
...Must evaluate to true. If not, AJS will throw an exception because the 'typeof' a function object equates to 'function'. Example Sixty (which does not work) illustrates this.

 // Example 60

 // Throws exception in AJS
 // Does not work in AJS_HP

 function YourFunc () { alert ("YourFunc executed"); }

 function MyFunc ()
    {
    function MyInner ()
       {
       alert ("MyInner executed");
       }

   MyInner ();

   }

 AJS.AddPrefix (MyFunc, "MyInner", YourFunc);

 MyFunc ();

 --------------------------------------

 Output (using AJS, and assuming exception is caught by the browser):

 Error: AJS.AddPrefix - IntercepteeOwner-argument does
 not refer to an object. Client-code call point: undefined

 --------------------------------------

 Output (using AJS_HP):

 MyInner executed
            
Closures as Affixes
Despite the restrictions on using inner functions as interceptees and/or affixes directly, it is certainly possible for a method to return a reference to an inner function, and then supply that to AddPrefix etc, or AddBefore/AddAfter.
Given that returning an inner function generates a closure, this (gratifyingly) means that closures can be used as prefixes and suffixes. The principle is very simple, and is illustrated in Example Sixty-One. Here GetInner is invoked to generate the third argument to AddPrefix, and the reference to MyInner that is returned forms the prefix-reference. Calling MyFunc therefore causes MyInner to execute first, after which MyFunc executes.

 // Example 61

 function MyFunc () { alert ("MyFunc executed"); }

 function GetInner ()
    {
    function MyInner () { alert ("MyInner executed"); }

    return MyInner;

    }

 AJS.AddPrefix (this, "MyFunc", GetInner ());

 MyFunc ();

 --------------------------------------

 Output:

 MyInner executed
 MyFunc executed
            
Note that it is not possible to intercept closures in themselves, as they are an artefact of the run-time engine, not of the 'classical' object space. However, it is possible to replace the reference that is associated with an object's method-name with a reference to an inner function (which is the principle that AspectJS exploits). Doing this makes it possible to use AspectJS to intercept the reassigned method. Example Sixty-Two illustrates this.
Furthermore, note that functions that create closures can receive parameters, which then form part of the closure (and it is this that makes closures so interesting). Note also that, given that closures can be used as affixes, it follows that co-routines (which can implemented in JavaScript using closures) can form affixes as well.

 // Example 62

 function Prefix () { alert ("Prefix executed"); }

 function GetInner ()
    {
    function MyInner () { alert ("MyInner executed"); }

    return MyInner;

    }

 var MyObj =
    {
    Method : function () { alert ("Method executed"); }

    };

 MyObj["Method"] = GetInner ();

 AJS.AddPrefix (MyObj, "Method", Prefix);

 MyObj.Method ();

 --------------------------------------

 Output:

 Prefix executed
 MyInner executed
            
Go forward to Part 11 of this tutorial.
Go back to Part 9 of this tutorial.
Copyright © Dodeca Technologies Ltd. 2007