I also will give a late answer in order to shed some light onto this special case that every then and now pops up as JavaScript and AOP.
Firstly, cases like the very one presented by the OP always ask for modifying already existing functionality, thus targeting closed code that sometimes is not even owned by the party that sees itself challenged from modifying the control flow of such code.
Why then, not just name it like that ... JavaScript method modification or JavaScript method modifiers.
Secondly, because of already riding the horse of terminology, altering closed functionality in JavaScript has nothing to do with Aspect-oriented Programming unless an implementation that claims to be AO provides abstraction and code-reuse levels for at least Aspect, Advice and Pointcut.
Last, for what the OP is going to achieve and what also has been the accepted answer, there does exist a a whole bunch of before
, after
around
/ wrap
solutions, almost always unfortunately mentioning AO(P), and in far too many cases not taking care of the context or target
which is essential to method modification.
The example I do provide uses a prototypal implementation of afterThrowing
. Because JavaScript already features a standardized bind
, I'm firmly convinced that Function.prototype
is the right place as well for some other method-modifiers
like before
, after
, around
, afterThrowing
and afterFinally
.
// OP's example pseudo code
//
// functionName.before("try {")
//
// functionName.after("} catch(ex){
// dostuff
// }")
function doStuffAfterThrowing(exception, originalArguments) {
"use strict";
var context = this;
console.log('context : ', context);
console.log('String(exception) : ', String(exception));
console.log('originalArguments : ', originalArguments);
return "safely handled exception";
}
function doFail() {
throw (new ReferenceError);
}
function oneOutOfManyAjaxCallbacks(payload) {
doFail();
}
var jsonData = {
"foo": "foo",
"bar": "bar"
};
var someModifiedAjaxCallback = oneOutOfManyAjaxCallbacks.afterThrowing(doStuffAfterThrowing, { x: 'y' });
// does fail controlled/handled.
console.log('someModifiedAjaxCallback(jsonData) : ', someModifiedAjaxCallback(jsonData));
// does fail "Uncaught".
console.log('oneOutOfManyAjaxCallbacks(jsonData) : ', oneOutOfManyAjaxCallbacks(jsonData));
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
(function (Function) {
var
isFunction = function (type) {
return (
(typeof type == "function")
&& (typeof type.call == "function")
&& (typeof type.apply == "function")
);
},
getSanitizedTarget = function (target) {
return ((target != null) && target) || null;
}
;
Function.prototype.afterThrowing = function (handler, target) { // afterThrowing
target = getSanitizedTarget(target);
var proceed = this ;
return (isFunction(handler) && isFunction(proceed) && function () {
var ret, args = arguments;
try {
ret = proceed.apply(target, args);
} catch (exc) {
ret = handler.call(target, exc, args);
//throw exc;
}
return ret;
}) || proceed;
};
}(Function));
</script>
Having come that far one might also consider reading ...