Tutorial — Intercepts in Practice
Part 8 of a Guide to Function Interception in JavaScript
Contents
Intercepting User-Defined Object Methods
Applying intercepts to user-defined object-methods is the same as intercepting global methods. If a method sets an intercept on another method of the object of which it is a member, then 'this' should be specified as the IntercepteeOwner argument. If it sets an intercept on a method of another object, however, then the name of that object should form the IntercepteeOwner argument. In Example Thirty-Four, the function Prefix is set as a prefix on the Method function of MyObj.
Importantly, affix functions are called 'in the context of' the object in whose context the interceptee is called. In other words, the value of the 'this' reference when the affix executes is the same as when the associated interceptee is executed.
This means that affixes have access to the same object-members that the interceptee does, and in Example Thirty-Four the prefix function is therefore able to output the value of the Name member of MyObj.

 // Example 34

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

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

 var MyObj = new Obj ("Homer");

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

 MyObj.Method ();

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

 Output:

 Prefix executed - Name = Homer
 Method executed - Name = Homer
            
Intercepting Object-Literal Methods
Objects defined using JavaScript's object-literal syntax are no different to instances of user-defined types created using a constructor, and their methods are intercepted in the same way. Example Thirty-Five uses the same example as before, but defines MyObj using object-literal syntax. Other than that, the example works in exactly the same way.

 // Example 35

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

 var MyObj =
    {
    Name   : "Homer",
    Method : function () { alert ("Method executed - Name = " + this.Name); }
    };

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

 MyObj.Method ();

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

 Output:

 Prefix executed
 Method executed - Name = Homer
            
Intercepting Built-In Type Methods
Methods of objects that are created by built-in constructors can be intercepted. In the example, an instance of the Date type is created, and a suffix is applied to the getYear method. When that function is invoked on the Date object, the suffix executes and outputs the time.

 // Example 36

 function Suffix (Arg, Args, Result)
   {
   alert ("Time = " + this.getTime ());
   }


 var MyDate = new Date ();

 AJS.AddSuffix (MyDate, "getYear", Suffix);

 alert ("Year = " + MyDate.getYear ());

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

 Output (in Firefox, and depending,
 obviously, on when the code is run):

 Time = 1171660796592
 Year = 107
           
Intercepting Constructors
Intercepts are particularly useful when applied to user-defined constructors as, not least, they allow parameters to be checked on entry to these functions, and allows the objects created to be examined on exit. This has clear potential in terms of tracing and debugging.
Given that prefixes and suffixes are always called in the context in which the interceptee is called (the 'this' reference has the same value), this means than attaching a suffix to the constructor will allow the suffix to examine the contents of the object that has been created.
No special coding is needed, as everything is managed by AspectJS, and this is demonstrated in Example Thirty-Seven, where a suffix is attached to the PeopleObj constructor. When the constructor executes, it initialises the new object with three attributes, and on exit the suffix is invoked, giving that function access to the object's members.

 // Example 37

 function PeopleObj (P1, P2, P3)
    {
    this.P1 = P1;
    this.P2 = P2;
    this.P3 = P3;
    }

 function ObjectExaminer ()
    {
    alert (this.P1);
    alert (this.P2);
    alert (this.P3);
    }

 AJS.AddSuffix (this, "PeopleObj", ObjectExaminer);

 var Obj = new PeopleObj ("Fred", "Barney", "Wilma");

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

 Output:

 Fred
 Barney
 Wilma
            
Intercepting user-defined constructors also offers an alternative to using prototype functions when adding a method or attribute to all objects of a particular class, it also offers an alternative to the use of constructor chaining. In Example Thirty-Eight the suffix adds extra names to the nascent PeopleObj.

 // Example 38

 function PeopleObj (P1, P2, P3)
    {
    this.P1 = P1;
    this.P2 = P2;
    this.P3 = P3;
    }

 function ObjectModifier ()
    {
    this.P4 = "Homer";
    this.P5 = "Marge";
    this.P6 = "Bart";

    }

 AJS.AddSuffix (this, "PeopleObj", ObjectModifier);

 var Obj = new PeopleObj ("Fred", "Barney", "Wilma");

 alert (Obj.P1);
 alert (Obj.P2);
 alert (Obj.P3);
 alert (Obj.P4);
 alert (Obj.P5);
 alert (Obj.P6);

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

 Output:

 Fred
 Barney
 Wilma
 Homer
 Marge
 Bart
            
Note that it seems that the ability to intercept calls to the constructors of built-in types should not be relied on. Example Thirty-Nine shows what, in principle, should work completely, but in practice does not. In Firefox 1 and IE 6 the interception can be applied, and when the prefix executes it has access to the parameter(s) passed to the Date constructor. However, when the suffix executes it is unable to invoke the getTime method of the newly created object.
If this is a problem when using AspectJS then a solution is to wrap the call to the built-in type constructor in a user-defined function, and then apply a suffix to that, where the suffix is configured to examine the function's return value.

 // Example 39

 // This does not work completely

 function Prefix (Arg, PrevPrefixResult, IArgs)
    {
    alert ("Prefix executed - IArgs[0] = " + IArgs[0]);
    }

 function Suffix ()
    {
    alert ("Suffix executed ");
    alert ("Time = " + getTime ());
    }

 AJS.AddWrapper (this, "Date", Prefix, "", Infinity, Suffix);

 var MyDate = new Date (1000);

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

 Output:

 Prefix executed - IArgs[0] = 1000;
 Suffix executed
            
Intercepting Exceptions
Given that user-defined constructors can be intercepted, it follows that the constructors of user-defined exception types can be intercepted as well. In Example Forty, a prefix is applied to an exception constructor called MyException. When the code throws a MyException the prefix executes first.
Certainly, this has no effect on the operation of the program or on the propagation of the exception, as it is not possible for prefixes or suffixes to control a system's execution-flow directly.
Nor does throwing an exception while another has yet to be caught have any effect; meaning that prefixes and suffixes cannot replace one exception object with another (nor, it would seem, crash the interpreter).
The primary value of intercepting exception-construction would therefore seem to lie in garnering extended error-information, allowing developers to know what exceptions are generated, while they let catch clauses in the application code swallow the exception objects silently.

 // Example 40

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

 function MyException (Message)
    {
    this.Message = Message;
    }


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

 throw new MyException ();

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

 Output (depending on browser):

 Prefix executed
 uncaught exception
            
Intercepting Prototype Functions
Functions defined in a prototype object can also be intercepted, meaning that interception of a call to such a function will occur irrespective of the object on which the function is executed. Example Forty-One shows this.

 // Example 41

 function Prefix () { alert ("This object's names are:"); }

 function PeopleObj (P1, P2, P3)
    {
    this.P1 = P1;
    this.P2 = P2;
    this.P3 = P3;
    };

 PeopleObj.prototype.OutputNames = function ()
    {
    alert (this.P1);
    alert (this.P2);
    alert (this.P3);
    }

 AJS.AddPrefix (PeopleObj.prototype, "OutputNames", Prefix);

 var Obj_1 = new PeopleObj ("Fred",  "Barney", "Wilma");
 var Obj_2 = new PeopleObj ("Homer", "Marge",  "Bart");

 Obj_1.OutputNames ();
 Obj_2.OutputNames ();

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

 Output:

 This object's names are:
 Fred
 Barney
 Wilma
 This object's names are:
 Homer
 Marge
 Bart
            
The methods of built-in types can also be intercepted using their prototype objects. In Example Forty-Two a prefix is applied to the prototype version of getYear, the result being that any call to getYear, on any Date object, causes the prefix to execute.

 // Example 42

 function Prefix () { alert ("Time = " + this.getTime ()); }

 AJS.AddPrefix (Date.prototype, "getYear", Prefix);

 var MyDate   = new Date ();
 var YourDate = new Date ();

 alert ("Year = " + MyDate  .getYear ());
 alert ("Year = " + YourDate.getYear ());

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

 Output (in Firefox, and depending on
 when the code is run):

 Time = 1171661557145
 Year = 107
 Time = 1171661557145
 Year = 107
           
Prototype Functions as Affixes
Prototype functions can also be used as prefixes and suffixes. In the example, every object created using the SimpsonsObj constructor has a prototype function that will output the names contained within the object in question.
The code sets a prefix on the Output method of an object created using the FlintstonesObj constructor, where the prefix function is the OutputNames prototype-method of a SimpsonsObj. It is this prefix that outputs the names of the FlintstonesObj, while FlintstonesObj.Output simply reports that Flintstones characters live in Bedrock.

 // Example 43

 function SimpsonsObj (P1, P2, P3)
    {
    this.Name_1 = P1;
    this.Name_2 = P2;
    this.Name_3 = P3;
    };

 SimpsonsObj.prototype.OutputNames = function ()
    {
    alert ("The names of some famous cartoon characters are as follows:");
    alert (this.Name_1);
    alert (this.Name_2);
    alert (this.Name_3);
    }

 function FlintstonesObj (P1, P2, P3)
    {
    this.Name_1 = P1;
    this.Name_2 = P2;
    this.Name_3 = P3;

    this.OutputNames = function ()
       {
       alert ("This lot live in Bedrock");
       }

    };


 var MyFlintstonesObj = new FlintstonesObj ("Fred", "Barney", "Wilma");

 AJS.AddPrefix (MyFlintstonesObj, "OutputNames", SimpsonsObj.prototype.OutputNames);

 MyFlintstonesObj.OutputNames ();

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

 Output:

 The names of some famous cartoon characters are as follows:
 Fred
 Barney
 Wilma
 This lot live in Bedrock
            
Intercepting Event-Handlers
There are a number of forms of event that can be generated within a JavaScript application. These include events generated by users of the application, such as mouse movements and button-actions, but events can also be generated under program control through the use of timers, and all of these event types can be intercepted using AspectJS.
In the first example, a prefix is applied to an event-handler function that is tied into mouse-clicks on a button embedded within the page. Each click on the button causes the prefix to execute first.

 <!-- Example 44 -- >

 <html>
    <head>
       <script type = "text/javascript" src = "AspectJS_Reference.js"></script>
       <script type = "text/javascript">

       function Prefix        () { alert ("Prefix executed"); }
       function ButtonClicked () { alert ("Event-handler executed"); }

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

       </script>
    </head>

    <body>

       <input type    = "Button"
              value   = "Click Me, Dear Aspectorian"
              onclick = "ButtonClicked ()" />

    </body>
 </html>

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

 Output:

 Prefix executed
 Event-handler executed
            
In the second example, a prefix is applied to a function that is to be called after 1.5 seconds. The timer is then set with the required interval, and the prefix and TimerFunc then execute in sequence repeatedly. Note that the intercept must be applied before the timer is set, otherwise the wrong function-reference will be passed to setTimeout (or setInterval, accordingly).

 // Example 45

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

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

 setTimeout    (TimerFunc, 1500);

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

 Output (after a 1.5-second delay):

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