I am needing to find the argument passed to a function from the function.
Let us suppose I have a function called foo
:
function foo() {
var a = 3;
var b = "hello";
var c = [0,4];
bar(a - b / c);
bar(c * a + b);
}
function bar(arg) { alert(arg) }
As it is now, of course, bar
will always alert NaN
.
Inside of the function bar
, I want to obtain the argument in the form it was originally passed. Furthermore, I want to be able to access the values of a
, b
, and c
from the bar
function. In other words, I would like something of this nature:
bar(a - b / c);
function bar() {
//some magic code here
alert(originalArg); //will alert "a - b / c"
alert(vars.a + " " + vars.b + " " + vars.c); //will alert "3 hello 0,4"
}
You may not think this is possible, but I know you can do weird things with Javascript. For example, you can display the code of the calling function like this:
function bar() {
alert(bar.caller);
}
I am willing to bet a few dollars that with some sort of finagling you can get a the original form of the argument and the values of the variables in the argument.
I can easily see how you could do it if you were allowed to call bar
in a form like this:
bar(function(){return a - b / c}, {a: a, b: b, c: c});
But that is too convoluted and therefore unacceptable. The way bar
is called may not be changed.
I am needing to find the argument passed to a function from the function.
Let us suppose I have a function called foo
:
function foo() {
var a = 3;
var b = "hello";
var c = [0,4];
bar(a - b / c);
bar(c * a + b);
}
function bar(arg) { alert(arg) }
As it is now, of course, bar
will always alert NaN
.
Inside of the function bar
, I want to obtain the argument in the form it was originally passed. Furthermore, I want to be able to access the values of a
, b
, and c
from the bar
function. In other words, I would like something of this nature:
bar(a - b / c);
function bar() {
//some magic code here
alert(originalArg); //will alert "a - b / c"
alert(vars.a + " " + vars.b + " " + vars.c); //will alert "3 hello 0,4"
}
You may not think this is possible, but I know you can do weird things with Javascript. For example, you can display the code of the calling function like this:
function bar() {
alert(bar.caller);
}
I am willing to bet a few dollars that with some sort of finagling you can get a the original form of the argument and the values of the variables in the argument.
I can easily see how you could do it if you were allowed to call bar
in a form like this:
bar(function(){return a - b / c}, {a: a, b: b, c: c});
But that is too convoluted and therefore unacceptable. The way bar
is called may not be changed.
6 Answers
Reset to default 6First of all — weird question. It would help a lot to know the context of why you are asking, because obviously Javascript doesn't have exactly what you are asking. (For example, is it something specifically to do with math expressions? Does the expression have to be executed prior to calling the function, as in your example?)
Javascript does not have:
- named parameters
- first-class/dynamic expressions
Javascript does have:
- dynamic objects
- first-class functions
- closures
eval
Dynamic objects
Since this was your own stated option at the end, that you declared unacceptable, it sounds like it won't help you much. In any case, since you want the expression and the values, just passing an object of the values alone isn't going to help you.
First-class functions
As you stated, you do have access to Function.caller
, although that just outputs a String representation of the whole function. It is also non-standard.
You also have arguments
, which is an Array of the arguments that were passed in, not named. In the case of your existing code, obviously, it will be the executed result of the expression. One convenient use of arguments
is not specifying any parameters in the function signature and then iterating through the array as an open-ended list.
Closures
This is also a part of your unacceptable solution at the end, but could possibly the closest to a working solution for you, though you'd have to be flexible on the way bar
is called or where it is defined.
function foo() {
var a = 3;
var b = "hello";
var c = [0,4];
var bar = function(arg) {
alert(arg); // NaN
alert(a + " " + b + " " + c); //will alert "3 hello 0,4"
}
bar(a - b / c);
bar(c * a + b);
}
eval
Using eval and closures, you might get close to what you want.
The primary thing to remember is that in your code, a - b / c
is an expression. That is not the argument, the result of the expression is the argument. There is nothing besides a toString
on the function itself that will alert "a - b / c".
With eval
(Disclaimer: this is very hacky and not recommended! You've been warned.), you could actually pass a String as an argument, and using a closure which is bound to the space, alert what you want.
function foo() {
var a = 3;
var b = "hello";
var c = [0,4];
function hackyUglyEvalAndOutput(callback, expression) {
alert(expression); //will alert "a - b / c" (duh)
alert(a + " " + b + " " + c); //will alert "3 hello 0,4" (because of the closure)
callback(eval(expression)); // "NaN"
}
hackyUglyEvalAndOutput(bar, "a - b / c");
hackyUglyEvalAndOutput(bar, "c * a + b");
}
function bar(arg) { alert(arg); }
You could similarly parse caller
to find calls to the function and see what was passed into the argument list (I did get it to do this) but you will have no way of distinguishing which one made the call if there is more than one. From your example, that would be crucial to accomplish what you want to do.
Again — expressions are just that and nothing more; they are not arguments.
P.S. The second call to bar
will actually output NaNhello
:)
Ok, I did it! (sort of) What you see below is the most naive, buggy, hideous, and hacky code I have ever written. This is the first time I have ever really used regex, and I'm not very good at making parser code.
Here we go:
function foo () {
var a = 3;
var b = "hello";
var c = [0, 4];
bar(a - b / c);
bar(c * a + b);
};
var callTracker = {};
function bar() {
var caller = bar.caller.toString();
callTracker[caller] !== undefined ? callTracker[caller]++ : callTracker[caller] = 0;
function findCall(str) {
return str.search(/bar\((\S*\s*)*\);/);
}
//Step 1: Get the orginal form of the argument
var callers = [caller];
var index = 0;
var len;
while (true) {
len = callers.length - 1;
index = findCall(callers[len]);
if (index === -1) break;
callers.push(callers[len].substring(index + 1));
}
var callIndex = callTracker[caller] % len;
var thisCall = callers[callIndex];
var argument = thisCall.substring(findCall(thisCall));
argument = argument.slice(4, argument.search(/\);/));
alert(argument);
//Step 2: Get the values of the variables
caller = caller.slice(caller.search(/\{/) + 1, -1);
var lines = caller.split(";");
for (var i = 0; i < lines.length; i++) {
lines[i] += ";";
if (findCall(lines[i]) === -1) {
eval(lines[i]);
}
}
var vars = argument.match(/\w/g);
for (var i in vars) {
if (vars.hasOwnProperty(i)) {
alert(eval(vars[i]));
}
}
}
foo();
I can forsee quite a few situations where this will break, but I'm too lighthearted to test them out. Just a couple examples: if the code contains a string literal with a semicolon in the middle, it will break, or if somebody relies on automatic semicolon insertion, it will break. If there is a conditional return
statement between the two calls to bar
, it will break, due to my extremely naive parsing.
But it does work for this example. If used in a real project, I think it would actually work most of the time, but there could end up being some really weird bugs.
If you want to play with it yourself, here it is on JsFiddle.
The final takeaway, I think, is never underestimate the power of a program that can read (and even change) itself.
Your question is misguided and sick and...I love it! Here is a short solution which I will explain.
function foo() {
var a = 3, b = "hello", c = [0, 4];
bar(a - b / c);
}
function bar(n) {
alert([n, a, b, c, eval(n)].join(' '));
}
function meld(f, g) {
f = f.toString(); g = g.toString();
f = f.replace(/function\s+\w+/, 'function ');
var n = g.match(/function\s+(\w+)/)[1];
f = f.replace(new RegExp(n + '\\((.*?)\\)', 'g'), n + '("$1")');
var i = f.indexOf('{') + 1;
f = f.slice(0, i) + g + f.slice(i);
eval('var ret = ' + f);
return ret;
}
foo = meld(foo, bar);
foo();
The key is the meld(f, g)
function which returns a new anonymous function that acts just like f
while calling and exposing its internals to a copy of g
. You see, the original g
is in a bad position to see anything about f
-- at best it can use g.caller
and tons of regular expressions.
Here is the pseudo code for meld
. First make f
an anonymous function, suitable for evaluating and assigning to a variable later on; for instance function foo() {
becomes function() {
. Next find g
's real name and quote any arguments passed to it from new f
. Next, insert a copy of g
inside new f
. It will mask the global name and it will have access to f
's local variables as if they were its own. Finally, evaluate this anonymous function and return it.
So how do we use meld
? Just replace foo
with meld(foo, bar)
and then use foo
as you normally would.
OK, now for the limitations. I didn't want to spend lots of effort refining the regex inside meld
to quote g
's argument(s) against all possibilities. This would just have been a distraction from the concept of the solution as a whole. You'll need to change it to properly handle more than one argument and escape any which themselves have quotes or parentheses.
Personally I was thinking of generating an exception and then follow the stacktrace but that would only work on firefox.. Fortunately someone wrote a stacktrace library which would always indicate which function is calling so that you can parse out the exact line that is calling.
https://github.com/eriwen/javascript-stacktrace/blob/master/stacktrace.js
My input to the topic:
http://jsfiddle.net/eWBpF/2/
function foo () {
var a = 3;
var b = "hello";
var c = [5, 2];
var d = new Date();
bar(a - b / c);
bar(c * a + b - d);
};
function bar(args)
{
var caller = bar.caller.toString();
if( typeof window.bar_call == 'undefined' )
{
window.bar_call = 0;
}
else
{
window.bar_call++;
}
var indexes = caller.match(/var \S* = [^;]+;/g);
var varValues = [];
for( var i=0; i<indexes.length; i++ )
{
var tmpVal = indexes[i].substring(indexes[i].search(/=/));
tmpVal = tmpVal.substring(2,tmpVal.length-1);
varValues.push(tmpVal)
}
var variables = varValues.join(" ");
var barCalls = caller.match(/bar\([^;]+\);/g);
var call = barCalls[window.bar_call];
call = call.substring(4, call.length-2);
alert(call + "\n\n" + variables);
}
foo();
This would be neat, but it is not possible.
The closest thing you can get is arguments
, which is simply an array of all the arguments to the current function.
You could enumerate this list and derive the types and values, and get the length so you know how many arguments - but the name and the operators will be lost.
See http://www.devguru.com/technologies/ecmascript/quickref/arguments.html
a - b * c
or whatever other expression into the actual value before passing it to the next function down. – jcolebrand Commented Jul 16, 2011 at 6:30