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