Arguments can be passed to an intercepted function in the usual (non-intercepted) fashion. These pass through the interception mechanism transparently, as Example 9 illustrates.
// Example 9
function prefixFunc () { alert ("prefixFunc executed"); }
function myFunc (Value)
{
alert ("myFunc executed. Argument passed = " + Value);
}
AJS.addPrefix (this, "myFunc", prefixFunc);
myFunc (42);
--------------------------------------
Output:
prefixFunc executed
myFunc executed. Argument passed = 42
Interceptees can return values to their callers in the same way as any other function; and the value/reference returned passes back up through the interception mechanism transparently, as one would expect.
Example 10 illustrates this.
// Example 10
function prefixFunc () { alert ("prefixFunc executed"); }
function myFunc (Value)
{
alert ("myFunc executed");
return 42;
}
AJS.addPrefix (this, "myFunc", prefixFunc);
var Value = myFunc ();
alert ("Function returned - Value = " + Value);
--------------------------------------
Output:
prefixFunc executed
myFunc executed
Function returned - Value = 42
The interception mechanism is also transparent to the propagation of exception objects. This means that exceptions that arise from within the interceptee pass up the call chain to its callee, where they can be caught in the usual manner.
The diagram illustrates this point.
Given this, Example 11 demonstrates catching an exception thrown from within an intercepted method.
// Example 11
function prefixFunc () { alert ("prefixFunc executed"); }
function myFunc (Value)
{
alert ("myFunc executed, throwing exception...");
throw new Error ("myFunc Exception");
}
AJS.addPrefix (this, "myFunc", prefixFunc);
try { myFunc (); }
catch (E)
{
alert ("Exception caught - Message contents = " + E.message);
}
--------------------------------------
Output:
prefixFunc executed
myFunc executed, throwing exception...
Exception caught - Message contents = myFunc Exception
Note that, if a method has a suffix, and that method throws an exception, the suffix will not be executed, as Example 12 shows.
// Example 12
function suffixFunc () { alert ("suffixFunc executed"); }
function myFunc (Value)
{
alert ("myFunc executed, throwing exception...");
throw new Error ("myFunc Exception");
}
AJS.addSuffix (this, "myFunc", suffixFunc);
try { myFunc (); }
catch (E)
{
alert ("Exception caught - Message contents = " + E.message);
}
--------------------------------------
Output:
myFunc executed, throwing exception...
Exception caught - Message contents = myFunc Exception;
Exceptions that are thrown by prefixes and suffixes also propagate up to the interceptee's caller, and are not caught by the interception mechanism (which differs from the behaviour of AspectJS prior to version 1.1).
In Example 13, invocation of myFunc causes prefixFunc to execute, which throws an exception. This escapes prefixFunc's scope, and is caught at the scope of the interceptee's caller, meaning that the interceptee never executes.
Note that if a suffix throws an exception then the interceptee is, by definition, guaranteed to have executed already and is therefore unaffected.
// Example 13
function prefixFunc ()
{
alert ("prefixFunc executed, throwing exception...");
throw new Error ("prefixFunc Exception");
}
function myFunc () { alert ("myFunc executed"); }
AJS.addPrefix (this, "myFunc", prefixFunc);
try
{
myFunc ();
}
catch (E)
{
alert ("Exception caught - Message contents = " + E.message);
}
--------------------------------------
Output:
prefixFunc executed, throwing exception...
Exception caught - Message contents = prefixFunc Exception
Any arguments passed in the call to the interceptee are also passed on to affix functions. These are passed as a single parameter that represents the 'arguments' array that results from the call to the interceptee.
This is an array-like object (although not a true JavaScript array), which means that affixes can use a subset of array syntax to access each of the elements, and make use of them from there.
Example 14 illustrates this idea.
This is clearly of use in policing conformity to pre-conditions within an application, which is a facet of 'Programming by Contract' or 'Design by Contract' (DbC), and that it is this principle that aJS_DbC exploits (covered in a separate section of this tutorial).
// Example 14
function prefixFunc (IArgs)
{
alert ("prefixFunc executed");
alert ("IArgs[0] = " + IArgs[0]);
alert ("IArgs[1] = " + IArgs[1]);
alert ("IArgs[2] = " + IArgs[2]);
}
function myFunc (A, B, C)
{
alert ("myFunc executed");
alert ("A = " + A);
alert ("B = " + B);
alert ("C = " + C);
}
AJS.addPrefix (this, "myFunc", prefixFunc);
myFunc ("Fred", "Barney", "Wilma");
--------------------------------------
Output:
prefixFunc executed
IArgs[0] = Fred
IArgs[1] = Barney
IArgs[2] = Wilma
myFunc executed
A = Fred
B = Barney
C = Wilma
Access to the interceptee's arguments allows prefixes to change their value before the interceptee ever sees them. Example 15 illustrates this principle.
Do remember that functions in JavaScript are first-class objects that can have attributes and methods (that are quite distinct from the local objects and inner-functions that arise through its execution). Given this, and given that one can augment objects with additional attributes and methods, it follows that one can augment any function that is passed to an interceptee before the interceptee encounters it.
Note also that, while an interceptee's arguments are also passed to any suffixes that it may have, this is of no consequence to the interceptee because it executes before the suffix. It is, however, also invaluable in using AspectJS to implement DbC techniques, and this is explored in the section entitled Interceptee Return-values and Suffixes
// Example 15
function prefixFunc (IArgs)
{
alert ("prefixFunc executed");
IArgs[0].Name_1 = "Fred"
IArgs[0].Name_2 = "Barney"
}
function myFunc (Names)
{
alert ("myFunc executed");
alert ("Names.Name_1 = " + Names.Name_1);
alert ("Names.Name_2 = " + Names.Name_2);
}
var Names =
{
Name_1 : "Homer",
Name_2 : "Marge"
};
AJS.addPrefix (this, "myFunc", prefixFunc);
myFunc (Names);
--------------------------------------
Output:
prefixFunc executed
myFunc executed
Names.Name_1 = Fred
Names.Name_2 = Barney
Further to this, it is possible for prefixes to add arguments to the call to the interceptee. Sadly, because the arguments array in JavaScript is not the same as the ordinary Array type, it does not support methods such as push and pop. Given this, it is necessary to use the length property to append extra parameters, moreover one must also update the length property explicitly.
Nevertheless, the technique works and is demonstrated in Example 16.
// Example 16
function prefixFunc (IArgs)
{
alert ("prefixFunc executed - adding argument...");
IArgs[IArgs.length] = 3;
IArgs.length++;
}
function myFunc (A, B, C)
{
alert ("myFunc executed : " + A + " " + B + " " + C);
}
AJS.addPrefix (this, "myFunc", prefixFunc);
myFunc (1, 2);
--------------------------------------
Output:
prefixFunc executed - adding argument...
myFunc executed : 1 2 3
In addition to receiving the interceptee's arguments (and return value, in the case of suffixes), affixes can receive a value that the original interception-setter specifies.
This is passed as a single parameter to addPrefix or addSuffix, and is passed on by the interception mechanism to the affix in question (as its second argument) upon invocation of the interceptee. In the case of addWrapper, two static arguments can be provided, one for the prefix and one for the suffix. Omission of the second argument, however, will cause addWrapper to use the first as a default.
Example 17 illustrates these points.
Note that the calling signature of affix functions that make use of this facility must carry the interceptee's arguments-array parameter (explored above) as their leading argument, even if they do not use it.
// Example 17
// Note IArgs is not used here but must be present as the leading parameter
// in the signature of the prefix function, otherwise it would be impossible
// to pick up the value stipulated in the call to addPrefix
function prefixFunc (IArgs, Arg) { alert ("prefixFunc executed - Arg = " + Arg); }
function myFunc () { alert ("myFunc executed"); }
AJS.addPrefix (this, "myFunc", prefixFunc, "42");
myFunc ();
--------------------------------------
Output:
prefixFunc executed - Arg = 42
myFunc executed
Obviously, it may be necessary at times to pass multiple static-values to a given affix. To effect this, the relevant objects should be subsumed within a single container-object, which should then be passed addPrefix, addSuffix or addWrapper.
Example 18 illustrates this principle.
// Example 18
function prefixFunc (IArgs, ContainerArg)
{
alert ("prefixFunc executed");
alert ("ContainerArg.Attribute_1 = " + ContainerArg.Attribute_1);
alert ("ContainerArg.Attribute_2 = " + ContainerArg.Attribute_2);
}
function myFunc () { alert ("myFunc executed"); }
var MyValues =
{
Attribute_1 : 10,
Attribute_2 : 20
}
AJS.addPrefix (this, "myFunc", prefixFunc, MyValues);
myFunc ();
--------------------------------------
Output:
prefixFunc executed
ContainerArg.Attribute_1 = 10
ContainerArg.Attribute_2 = 20
myFunc executed
As with prefixes, suffixes are passed the interceptee's arguments array, and any static value that the intercept-setter stipulated, but they are also passed the value that the interceptee returns.
This allows a suffix to compare what went into its interceptee with what came out, and this is clearly of use in policing conformity to post-conditions, which is also an element of the DbC approach.
Example 19 demonstrates this by testing the value returned from a function that is supposed to calculate the area described by the Width and Length parameters that it is passed (but which has a deliberate bug).
Do note that, in this example, the Arg and PrevSRtn arguments in the signature of suffixFunc are ignored, but must be present in the code in order to allow the suffix to pick up the interceptee's result.
Note also that PrevSRtn ('Previous Suffix Return') forms the third parameter because AspectJS supports the concept of applying multiple affixes to a method. The rationale behind affix-function argument ordering is therefore explained in the section entitled Multiple Suffix Function-Signatures.
// Example 19
function suffixFunc (IArgs, Arg, PrevSRtn, IRtn)
{
alert ("suffixFunc executed");
if (IArgs[0] * IArgs[1] !== IRtn)
{
alert ("calcArea is flawed!");
}
}
function calcArea (Width, Length)
{
return Width + Length;
}
AJS.addSuffix (this, "calcArea", suffixFunc);
calcArea (10, 20);
--------------------------------------
Output:
suffixFunc executed
calcArea is flawed!