2

I want to do an AOP-like 'before' functionality for JavaScript functions.

So I looked for existing solution and found the aop-plugin for jQuery. Unfortunately the plugin simply wraps the given function. So any object that points to the original function before enhancing the function, still points to the original function. Calling the function from these objects calls the not enhanced version of the function.

Instead of wrapping functions with other functions, I want to change the function object itself, so previous references do not cause unanticipated behaviour. So I got something like this:

(function(){}).constructor.prototype.before = function(beforeFunction){
    this.beforeFunction = beforeFunction;
};

/* some magic should happen here: enhance call? */

function logFoo() {
    console.log("Foo");
};
function logBar() {
    console.log("Bar");
};

logFoo(); // logs "Foo"

logFoo.before(logBar);

logFoo(); // should log "Bar", then "Foo"

So the question is, how do I get the beforeFunction invoked, when the enhanced function is called?

Peter Seliger
  • 11,747
  • 3
  • 28
  • 37
Stefan Ramson
  • 531
  • 1
  • 6
  • 18
  • Regarding the former `AOP` tag, wrapping and reassigning already declared functionality (be it functions or methods) misses any aspect of _AOP_. Any language which wants to qualify for the latter has to provide abstraction levels for at least `Joinpoint`, `Advice` and `Aspect`. The use case described by the OP should be referred to as method modification, and JavaScript of cause is well suited for this scenario and could easily provide a complete `target`/`context` aware toolset of method modifiers like `around`, `before`, `after`, `afterThrowing` and `afterFinally` via `Function.prototype`. – Peter Seliger Sep 14 '22 at 17:39

1 Answers1

6

The body, parameters and referencing environment of a function are immutable so you cannot enhance a function, you can only make new functions.

You could make your function enhanceable from the beginning like this:

Function.prototype.enhanceable = function() {
    var original = this,
        enhanced = [],
        ret = function() {
            for( var i = 0; i < enhanced.length; ++i ) {
                enhanced[i].apply( this, arguments );
            }

            return original.apply( this, arguments );
        };

    ret.before = function( fn ) {
        enhanced.push( fn );
    };

    return ret;
};

Then:

var logFoo = function() {
     console.log( "foo" );
}.enhanceable();

var logBar = function() {
     console.log( "bar" );
};

logFoo(); //foo

logFoo.before( logBar );

logFoo(); //logs bar, then foo
Esailija
  • 138,174
  • 23
  • 272
  • 326