6

Is there a way to get the variable name, like you can do in .Net with reflection?
like in this scenario:

function(x,y,z)
{
    if (x === 0)
        logger.log('variable ' + x.reflectedName ' has invalid value ' + x)
        // logs: 'variable x has invalid value 0)
    ...
}

I found similar questions that wanted the name of the var outside of the function(?!) but couldn't find this question.

(jQuery is an option, though I can't think how it can be done with it...)

Pointy
  • 405,095
  • 59
  • 585
  • 614
gdoron
  • 147,333
  • 58
  • 291
  • 367
  • 7
    Worth pointing out: you cannot get a variable name via reflection in .NET. – StriplingWarrior Mar 20 '12 at 22:11
  • @StriplingWarrior. you can with `Expression` I simplify it for none .Net developers... – gdoron Mar 20 '12 at 22:12
  • 1
    why not just log your variable name in the string? in your example, it'd be just as easy to put x in the string `console.log('variable x has invalid value:', x)` – SpYk3HH Mar 20 '12 at 22:12
  • 1
    @SpYk3HH. One reason is, if I change the `x` variable name to `foo`, I won't need to change all those strings to `foo` – gdoron Mar 20 '12 at 22:16
  • 1
    You would still have to change the variable call though, so isn't it the same? – SpYk3HH Mar 20 '12 at 22:20
  • 1
    @SpYk3HH. calling `x` when the variable name is `foo` will give me error. log `"variable x has invalid value"` won't. – gdoron Mar 20 '12 at 22:23
  • @gdoron, then you're not looking for the name of the variable. That would just give you the formal parameter name (you can't "call x"; the variable name will *always* be x inside this function, even if you called the function passing in a variable named "foo" -- *that's a different variable*). You are looking for the "name of the actual parameter associated with the formal parameter denoted by 'x'". There's no way to do that in JS. Many languages with more reflection can't even do that (some can). – Ben Lee Mar 20 '12 at 22:28

4 Answers4

8

Actually you CAN. Here is a snippet:

function getVarName(v) {
    for (var key in window) {
        if (window[key] === v)
            return key;
    }
}
var testvar = 13142;
function test(t) {
    var varName = getVarName(t);
    // window[varName] -- your variable actualy
    alert(varName);
}
test(testvar); // testvar

The other problem is if you create some vars that contain the same variable. Then first var will be returned.

ovnia
  • 2,412
  • 4
  • 33
  • 54
  • 5
    `var testvar = 13142;var testvar2 = 13142;` or `var testvar = true;` or anything that is an object will ruin your day. Anything not defined in the windows global scope will also ruin your day. – Дамян Станчев Jun 23 '14 at 08:53
7

Since you're using .NET as an example, let's delve briefly into that. In C#, you could create a function that takes an Expression:

void BadArgument<T>(Expression<Func<T>> argExpr)
{
}

But in order to be able to extract the variable name from a call to this function, you would have to make sure the call always uses exactly the right syntax (even though there is no way to enforce this at compile time):

if(x < 0)
    BadArgument(() => x);

So it can be done, but it's very fragile and pretty slow. You're basically generating instructions to create a whole expression tree based on the lambda expression () => x, just so the function you call can parse out that expression tree and try to find the name of the argument.

Can this sort of thing be done in javascript? Sure!

In javascript, closures are produced via internal functions, so the equivalent of the above lambda expression would be:

function(){return x;}

And since javascript is a scripting language, each function is the equivalent of its own definition as a string. In other words, calling .toString() on the above function will yield:

function(){return x;}

This jsfiddle shows how you can leverage this in a logging-style function. You are then free to parse the resulting function string, which will be only slightly more trouble than parsing the .NET Expression Tree would be. Furthermore, getting the actual value of x is even easier than in .NET: you just call the function!

But just because you can do it doesn't mean you should. It's nice as a gee-whiz parlor trick, but when it comes right down to it, it's not worth it:

  • It's fragile: what if some developer doesn't use it right and gives you a function that you can't parse?
  • It doesn't work with minification: imagine getting a message that variable a had an incorrect value because your minified function changed your variable names.
  • It adds overhead: even a minifier can't shorten function(){return x;} to be smaller than "x".
  • Finally, it's complicated. 'nuff said.
Praveen Kumar Purushothaman
  • 164,888
  • 24
  • 203
  • 252
StriplingWarrior
  • 151,543
  • 27
  • 246
  • 315
  • **Excellent!** I won't use it as you suggested, but it's an excellent answer! BTW what regular expression would you use to get `x` out of `function(){return x;}`? – gdoron Mar 21 '12 at 09:53
  • 1
    @gdoron: I don't know whether I'd use regex or not. Basically you just want to take everything after "return" and before "}", and strip any spaces or semicolons, and say a quick prayer that the calling code didn't throw you a curveball. – StriplingWarrior Mar 21 '12 at 15:10
  • **Caution!** be careful about `minification`, when you _minify_ your *.js files variables name may change. @gdoron – Iman Mahmoudinasab Dec 29 '14 at 06:25
  • @ImanMahmoudinasab: I already mentioned that as the second reason not to use this approach. – StriplingWarrior Dec 29 '14 at 13:48
6

You can't. But since you already know the variable name (since you have to use it to concatenate it to the end of the string), why not just type it in?

ie.:

logger.log('variable x has invalid value '+x);
Niet the Dark Absol
  • 320,036
  • 81
  • 464
  • 592
  • 3
    I think the example was for simplicity. I assume the OP wants to do this for cases where the variable name isn't known in advance. But +1 for "You can't." – Sasha Chedygov Mar 20 '12 at 22:12
  • What if someone calls the function with their own variables: `function(a, b, c)`, `x` would be meaningless to them. – Tim Withers Mar 20 '12 at 22:13
  • @musicfreak, there is no such instance where the variable name isn't known in advance. Think about it and you'll see what I mean. – Ben Lee Mar 20 '12 at 22:13
  • @gdoron, then please post what you actually mean. If your variable name is somehow dynmaic, then there is *some way* to get at it. If it is static, then just include it literally. – Ben Lee Mar 20 '12 at 22:14
  • One reason is, if I change the x variable name to `foo`, I won't need to change all those strings to `foo` – gdoron Mar 20 '12 at 22:16
  • 1
    @gdoron, you'll have to change them in both places regardless. If there was some reflection method, it would always be true that `x.variable_name === "x"`. So `if (x === 0) echo "x is 0"` has two places to change it and so does `if (x === 0) echo x.variable_name + " is 0"`. – Ben Lee Mar 20 '12 at 22:17
  • 1
    The example of when it's not known in advance would be a simple variable logger `function logVar(value) { console.log('Variable ', value.name, 'has value of ', value)}`, but yes, the answer is it can't be done. Note that for functions, you could possible do it. `function logFunctionContents(fn){console.log('Function ', fn.name, 'has content', fn.toSource())}` – Ruan Mendes Mar 20 '12 at 22:19
  • 1
    If you mean to cache it ahead of time with `var name = x.variable_name` you can just do `var name = 'x'`. *Any place you would do `x.variable_name`, just put the literal `"x"` instead and it will work exactly the same way.* – Ben Lee Mar 20 '12 at 22:19
  • @JuanMendes, `value.name` will always evaluate to `"value"`. – Ben Lee Mar 20 '12 at 22:20
  • @BenLee Of course, that is why I said it can't be done, but that is the idea the OP is looking for – Ruan Mendes Mar 20 '12 at 22:24
1

It is easy to get the variable name

//This is variable you want to get the name
var prop;

var obj = {prop};
for(var propName in obj){
  alert(propName);
  break;
}

also you can try in here.

Edited

Thanks for the comment @Brady

Object.keys({variableName})[0] will give you the variable name.

function(x,y)
{
    const key = (obj) => Object.keys(obj)[0];

    if (x === 0)
        logger.log('variable ' + key({x}) + ' has invalid value ' + x)
        // logs: 'variable x has invalid value 0)
    if (y === 0)
        logger.log('variable ' + key({y}) + ' has invalid value ' + y)
        // logs: 'variable y has invalid value 0)
}
小广东
  • 1,151
  • 12
  • 20
  • I don't think you got the question. Read other answers or the question again. Thanks regardless – gdoron Jun 01 '15 at 09:55
  • 2
    Actually, I think this does answer the question and is a clever approach. Here, you wrap the variable in an outer object, using the variable name as the property name. Then you grab the first property of the object which would be the name. This could be simplified as: `Object.keys({prop})[0]`. – Brady Holt Apr 13 '18 at 14:14