ritter.vg
tech > code > adventures in code > fun javascript
11 Oct 2009 11:08:23 EST
rewriting javascript functions at runtime

I recently had the necessity of rewriting a javascript function in javascript, dynamically. The ease with which I did it, and how fun it was, astounded me. Over here I've got some HTML:

<div id="excelExport1234" 
     onclick="if(somestuff) location.href='http://server/excelExport.aspx?id=56789&something=else'; else alert('not important');"
  >Click here to export to excel</div>
]]>~

And I couldn't change the outputted HTML, but I needed to add an extra parameter to that link. I started thinking about it, and realized I could just do this:

excelExport = $('excelExport1234');
if (needParam)
	eval('excelExport.onclick = ' + excelExport.onclick.toString().replace("excelReport.aspx?id", "excelReport.aspx?extraParam=true&id") + ';');
else
	eval('excelExport.onclick = ' + excelExport.onclick.toString().replace("extraParam=true&", "") + ';');
]]>

Now I know this is always a bad idea, and I know I'm breaking all sorts of rules, and the like - but the point is that is cool. I'm dynamically changing the function at runtime. I posted the example and the request for more in other languages on stackoverflow and was ultimately disappointed with the answers.

int[] numbers = {1, 2, 3, 4, 5};

Func<int[], int> somefunc;
if (someCondition) 
{
   somefunc = (is => is.Sum());
} else {
   somefunc = (is => is.Count());
}

Console.WriteLine(somefunc(numbers).ToString());
]]>

This example is no good - I'm defining two functions at compile-time and switching between them. I can do the same thing in C/C++, and other answers provided examples in python, and lisp. That's function pointers and that's not interesting at all.

create or replace procedure test
as
begin
 execute immediate '
create or replace procedure test2
as
begin
  null;
end;
 ';
end;
]]>

Now this example is much better. I haven't even considered SQL - but you can rewrite a stored procedure in PLSQL and T-SQL by getting itself and doing string manip - just like in javascript. And you can do that inside the SP, gaining yourself the self-modification badge.

Lisp is the quintessential example of self-modifiying, or degenerate, code. The thing is - you can't do it! That's right Lisp, I'm looking at you. Here's my gauntlet, and you can't meet it: Given a function name, dynamically, at run-time, alter the function to change it's behavior based on user input and the current state of the function. Stated another way: Change "f(x) = x + 5" to add 6. Without using variables, or currying, or anything of the like - I want full control, I want string manip!

I took it to the masters and the answer is... you can't do it without resorting to hackery. Now there are some ways to hack at it.

(define add-x-code
  '(lambda (n)
     (+ 3 n)))

(define add-x (eval add-x-code))

(define add-x-2 (eval (replace 3 7 add-x-code)))
]]>

This is ingenuitive, and the guy gets an upvote, but having to write all my code in quotes loses all sorts of editor niceness and adds ugly.

Rainer gives the best answer - Common Lisp throws out the source code. So your options are to either store the source (as demo-ed above) or use some hackery. He explains a way to "quasi" recover the source. It produces this:

CL-USER 12 > (function-lambda-expression #'addx)

(LAMBDA (N)
  (DECLARE (SYSTEM::SOURCE-LEVEL #<EQ Hash Table{0} 217874D3>))
  (DECLARE (LAMBDA-NAME ADDX))
  (+ N 3))
NIL
ADDX
]]>

That doesn't quite meet my definition of 'parsable' especially because of the extra symbols you can't always predict it adding.

So I'm left with one conclusion: javascript is best damn language out there.

Yes, I'm fully aware I should be burnt at the steak stake (mmmm steak) for ever considering using string manip on function objects to change their meaning in javascript. Do not try this at home kids (or if you do, try it only at home and never in any code that anyone else will ever run/care about). Yes, I know javascript isn't the best language in the world. Yes, I know the sky is blue, that unicorns aren't real, and that women are just as good at math as men. But I draw the line in the sand at saying this isn't cool. This is damn cool.

Comments
Add a comment...
required
required, hidden, gravatared

required, markdown enabled (help)
you type:you see:
*italics*italics
**bold**bold
[stolen from reddit!](http://reddit.com)stolen from reddit!
* item 1
* item 2
* item 3
  • item 1
  • item 2
  • item 3
> quoted text
quoted text
Lines starting with four spaces
are treated like code:

    if 1 * 2 < 3:
        print "hello, world!"
Lines starting with four spaces
are treated like code:
if 1 * 2 < 3:
    print "hello, world!"