6

In JavaScript, is it possible to insert a line into a function that already exists? I want to create a function that inserts a line at a specific position in a function:

function insertLine(theFunction, lineToInsert, positionToInsert){
    //insert a line into the function after the specified line number       
}

For example, would it be possible to programmatically insert the line checkParameterTypes(min, "string", max, "string"); before the first line of this function?

function getRandomInteger(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}
12
  • 3
    I really doubt if you can "insert" lines into a function. But, surely, you can wrap that function inside another one that calls your checkParameterTypes(..) before invoking getRandomInteger(..). Commented Apr 25, 2013 at 3:29
  • 1
    @Thrustmaster, You definitely can insert a line by getting the function's code, putting one in, and redefining it. But why would you want to? Duck punching, as you have proposed, is the way to do this. Commented Apr 25, 2013 at 3:33
  • @Brad The eval/Function constructor would evaluate it in the global scope though, losing access to all local variables. Sure it may suffice for some use cases, but is not ideal. Commented Apr 25, 2013 at 3:34
  • 1
    @FabrícioMatté What do you mean "evaluate it in the global scope"? I don't think it does. Are you thinking of setTimeout? Commented Apr 25, 2013 at 4:57
  • 1
    @FabrícioMatté Yep, I learned about Function's scope today, so we all learned :) But yeah, I'd avoid both, and you're right that it's cumbersome, I just wanted to point it out that it could be done to access local variables. Either way, the accepted answer here seems good and doesn't use either :) Commented Apr 25, 2013 at 16:55

2 Answers 2

5

If you want something to happen at the beginning of a function, you can use the following. You do have access to this and the arguments from your injected function. So it will still work for functions that require a specific context.

function inject(before, fn) {
    return function(){
        before.apply(this, arguments);
        return fn.apply (this, arguments);
    }
}

For example

function add(a, b) {
   return a + b; 
}    

function concat(a, b) {
    return a + b;
}

/**
 * You can repeat index and type to check multiple arguments
 */
function createArgumentChecker(index, type /**index, type, ... */) {
    var originalArgs = arguments; 
    return function() {
        for (var i=0; i < originalArgs.length; i+=2) {
             var index = originalArgs[i],
                 requestedType = originalArgs[i+1],
                 actualType = typeof arguments[index];
             if (typeAtIndex  != actualType) {
                 console.log("Invalid argument passed at index " + index  + 
                     ". Expected type " + requestedType +  "but it's " + actualType );
             }
        }
    }
}

function logArguments() {
    console.log(this, arguments);
}

// Inject an argument checker
add = inject(add, createArgumentChecker(0,"number", 1, "number"));
concat = inject (concat, createArgumentChecker(0, "string", 1, "string"));

// You can even do it multiple times, inject an argument logger;
add = inject(add, logArguments);
concat = inject(concat, logArguments);

JSfiddle

This can be handy when debugging websites that you can't modify the source code, I wouldn't use it do parameter checking unless you can strip it our for the production version.

Sign up to request clarification or add additional context in comments.

Comments

2

Yes you can but using eval is always evil ;)

function insertInbetween (arr, value, index) {
  var inserted, i, newarr = [];
  for (i = 0; i < arr.length; i++) {
    if(i == index && !inserted) {
      newarr[i] = value;
      inserted = true;
    }
    newarr.push(arr[i]);
  }
  return newarr;
}

function test (a, b) {
    console.log(a,b);
}

var fstrarr = test.toString().split('\n');
eval(insertInbetween(fstrarr, "console.log('injected!');", 1).join('\n'));

Edit: As mentioned in the comments to your question you'll loose scope by doing so.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.