Tutorial — Tips and Tricks
Part 11 of a Guide to Function Interception in JavaScript
Contents
Object-Property Enumeration
JavaScript allows the enumeration of an object's methods and attributes. When doing this in conjunction with using AspectJS, however, it is important to remember that, if a method has been intercepted, its 'value' will not be the same as it usually is. Example Sixty-Three illustrates this.
Here the members of MyObj are enumerated, after which a prefix is applied to its second method. Enumerating the object's members again then gives the contents of the interception mechanism set up by AspectJS (although this has been replaced here with an ellipsis for the purposes of clarity).
The important point is that while such a change may be fairly obvious in this example, it can create great confusion when it occurs with larger objects that have 'real' functions.

 // Example 63

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

 var MyObj =
    {
    Name_1   : "Fred",
    Name_2   : "Barney",

    Method_1 : function () { alert (this.Name_1); },
    Method_2 : function () { alert (this.Name_2); }

    };

 for (var Property in MyObj) { alert (Property + ": " + MyObj[Property]); }

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

 for (var Property in MyObj) { alert (Property + ": " + MyObj[Property]); }

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

 Output:

 Name_1: Fred
 Name_2: Barney
 Method_1: function () { alert (this.Name_1); }
 Method_2: function () { alert (this.Name_2); }

 Name_1: Fred
 Name_2: Barney
 Method_1: function () { alert (this.Name_1); }
 Method_2: function () { ... }
            
Swapping AJS for AJS_HP
Situations can arise where high performance is required, and only single prefixes, suffixes or wrappers are needed for the relevant functions in the application. This would suggest that AJS_HP is the natural choice for both development and deployment.
However, this is untrue because AJS performs rigorous argument checking and error reporting. Given this, it is favourable to use that object during development, and thus catch mistakes quickly, and to then use AJS_HP when deploying the system.
Obviously, the switch between the two can be effected by performing a global search-and-replace. However, this may be tedious when all that is needed is a quick test using the one version of the technology before reverting to the other version for more development work.
This can be resolved easily, however, by simply assigning a reference to either the AJS object (or the AJS_HP object), and then calling the relevant functions through that. When the calls should be routed through the other object, re-assign the reference to point to that. Example Sixty-Four illustrates this.
Note that this method incurs a small time-overhead because the reference to the relevant AspectJS object must be traversed by the interpreter before a given method is called. If this is a matter for concern then the only solution is to do a global search-and-replace on all affected files, replacing every instance of the reference with 'AJS_HP'.

 // Example 64

 // Development configuration

 var AspJSRef = AspectJS;
 //var AspJSRef = AspectJS_HP;

 AspJSRef.AddPrefix ( ... );       // AspectJS version of AddPrefix
                                   // will execute

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

 // Deployment configuration

 //var AspJSRef = AspectJS;
 var AspJSRef = AspectJS_HP;

 AspJSRef.AddPrefix ( ... );       // AspectJS_HP version of AddPrefix
                                   // will execute
            
Abusing Intercepts
It is possible to abuse function interception, and a parallel can be found in the abuse of exception handling. For example, it is possible to implement looping using the try/throw/catch mechanism, and Example Sixty-Five illustrates this (which is JavaScript, but with appropriate changes could equally be C++/Java/C# etc).
Here the function Iterate does something of interest, and then increments a variable called Count, before testing its value. If Count is less than a given limit it returns to MyFunc, whereupon it is called again. If, however, the value of Count reaches the limit then an exception is thrown, which breaks the execution thread out of the loop, thus allowing it to proceed from there.
This is out-and-out abuse of the exception-handling mechanism for the following reasons:
First, for a counting variable to reach a particular limit is not an 'exceptional' condition — a try/throw/catch construct is therefore inappropriate here, and can only confuse developers who expect to see it used in an entirely different context.
Secondly, the try/throw/catch mechanism incurs a run-time overhead that is disproportionate to the least-possible-cost of the simple iteration that it is being used to implement.
Finally, it incurs a large code overhead, thus swelling the total code-volume. This increases compile times (in a compiled language), and increases download and run times in contexts such as client-side JavaScript.

 // Example 65

 var Count = 0;

 function Iterate ()
   {
   alert ("Iteration " + Count);

   Count++;

   if (Count > 2) { throw new Error (); }

   }

 try
    {
    while (true) { Iterate (); }
    }

 catch (E) { }

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

 Output:

 Iteration 0
 Iteration 1
 Iteration 2
            
Exception-mechanism abuse indicates poor understanding of programming, and is a lazy approach to the challenges that software design and implementation present. As Example Sixty-Six shows trivially, programming languages support iteration adequately without the use of an exception mechanism.

 // Example 66

 function MyFunc ()
    {
    for (Count = 0; Count < 1000; Count++) { SomethingOfInterest (); }

    }

 function SomethingOfInterest ()
    {
    // ...
    }
            
In parallel with the above scenario, the use of method interception is also open to abuse; and Example Sixty-Seven shows a thoroughly conventional chain of function calls, where A calls B, and B calls C thereon.

 // Example 67

 function MyFunc () { A (); }

 function A ()
    {
    alert ("Func_A");
    B     ();
    }

 function B ()
    {
    alert ("Func_B");
    C     ();
    }

 function C ()
    {
    alert ("Func_C");
    }


 MyFunc ();

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

 Output:

 Func_A
 Func_B
 Func_C
            
As an alternative, one could create a chain of prefixes (or suffixes) that were attached to an empty function. Calling that function would then cause the execution thread to descend the list of affixes before returning to the interceptee. Example Sixty-Eight illustrates this.
As with the exception handling example, this is a redundant approach to the problem. It is abuse of a powerful technique, and is abuse of a key run-time mechanism. One might argue that using intercepts allows the functions and their calling order to be determined dynamically, but adequate solutions to that requirement exist in the form of object decoration etc, which use far more conventional and lighter-handed JavaScript.

 // Example 68
 // Works only in part in AJS_HP

 function MyFunc () { }

 function A      () { alert ("Func_A"); }
 function B      () { alert ("Func_B"); }
 function C      () { alert ("Func_C"); }

 AJS.AddPrefix (this, "MyFunc", A);
 AJS.AddPrefix (this, "MyFunc", B);
 AJS.AddPrefix (this, "MyFunc", C);

 MyFunc ();

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

 Output:

 Func_A
 Func_B
 Func_C
            
Go back to Part 10 of this tutorial.
Copyright © Dodeca Technologies Ltd. 2007