Home > Enterprise >  How to "append" an action to a function?
How to "append" an action to a function?

Time:09-25

I wanted to create a function that I can add actions to later. I tried this:

Function.prototype.appendAction = function(action) {
  let _ogFn = this
  return function(...args) {
    _ogFn(...args)
    action()
  }
}
function a() {
  console.log("foo")
}
a()
a = a.appendAction(() => console.log("bar"))
a()

This does work, but how can I make it change the function automatically? I want to make it work like this:

a()
a.appendAction(() => console.log("bar")) //note it doesn't have the "a = "
a() //changed function

CodePudding user response:

If you just want to call it immediately:

Function.prototype.appendAction = function(action) {
  let _ogFn = this
  return (function(...args) {
    _ogFn(...args)
    action()
  })()
}
function a() {
  console.log("foo")
}
a.appendAction(() => console.log("bar"))

That is the immediately invoked function expression (IIFE) pattern

CodePudding user response:

For your purposes you could use eval:

First, grab the user input however you are handling it and parse it as a string.

Then use eval like so:

let userInput = 'console.log("hello world")';

const callUserFunction = (action) => {
  eval(`function fn(){${action}}`);
  fn();
}

callUserFunction(userInput);

Be aware that allowing a user to write or JS is a security risk, which can open doors to all kinds of attacks, so in most scenarios, I would discourage doing so

CodePudding user response:

Depending on your scenario you could create a function decorator. This would mean defining a function (for example fnMod) that accepts a function fn and returns a decorated version that executes some before and after hooks.

const a = fnMod(() => {
  console.log("foo");
});

a();
a.appendAction(() => console.log("bar"));
a();



function fnMod(fn) {
  const before = [];
  const after = [];
  
  function hookable(...args) {
    before.forEach(fn => fn.apply(this, args));
    const result = fn.apply(this, args);
    after.forEach(fn => fn.apply(this, args));
    return result;
  };
  
  hookable.prependAction = (fn) => {
    before.unshift(fn);
  }
  
  hookable.appendAction = (fn) => {
    after.push(fn);
  }
  
  return hookable;
}

You might want to tweak it to your liking. Currently both this and all arguments passed to the decorated function are forwarded to all hooks, which might or might not be desirable.

  • Related