0

WARNING: As other have stated here, this problem is based upon an inflexible customer requirement. While the question is valid, you should definitely use the simpler solutions (ex: putting your settings in a single object) if possible!

I have an array of variables I want to be "watching out" for. That is: if they were previously defined in the code, I need to be able operate on their values.

var a = 'foo'; // only a gets defined

// ...more code here... IMPORTANT: I can only control what comes BELOW this line!

var target = 'exmple.com/?';
var gets = ['a', 'b', 'c']; // these are the names of the variables I want to check for

gets.forEach(function(element) { // checking each one
   if(typeof element !== 'undefined') { // if it's previously defined
  target += "&"+element+"="+eval(element); // add `&a=foo` to target
}
});

alert(target);

I'd like the alert to be printing example.com/?&a=foo

I've seen the tons of other answers not to use the eval function, and I have tried window[element] in place without success. How would it be best to do this without having to write an if/else for every variable I want to check?

Bing
  • 3,071
  • 6
  • 42
  • 81
  • 3
    *I have an array of variables I want to be listening for,* <-- What does that mean? – Scott Marcus Dec 06 '19 at 19:44
  • In my example there may have been variables with the names `a`, `b` or `c` previously declared in the code. If they are set (not undefined), then I want to operate on their values. In the example since `a` is set, I want to append its value (`foo` to my `target` string). – Bing Dec 06 '19 at 19:49
  • So, you are really just saying that you want to do something if any of the variables with names in your array is changed? – Scott Marcus Dec 06 '19 at 19:51
  • No, I want to get the value from the variable if it was defined. So previously in the code if `var a = 'Something';` was written, then `a` _has been previously defined_, and I now want to get it's value: `Something` – Bing Dec 06 '19 at 19:52
  • Feels like an [XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem)--why do you need to do this acrobatic maneuver? Can you just `if (foo !== undefined) { do stuff with foo }`? – ggorlen Dec 06 '19 at 20:00
  • 1
    The customer requested the ability to do it this way. They are using a JS file I wrote to embed some content on their page and they want to be able to control certain parameters by defining variables before injecting the JS file. So while there may be 100 other ways to solve it, this is how the customer wants it, hence my asking. Not pretending this is best practice, but this is the Q&A place for technical questions, not customer management ones, right? :-P – Bing Dec 06 '19 at 20:04
  • @Bing Fair enough. Although it may help to know what exactly is defined by both parties. If I understand correctly, you have one JS file where the top is authored by the customer and the bottom by yourself? – customcommander Dec 06 '19 at 20:11
  • @customcommander Effectively, yes. Really they're writing some JavaScript options (ex: `var color = 'red'; var link = 'google.com';`) which I am then checking on in an included JS file. If defined, I obviously use the settings as supported. For the sake of the question I simplified it to be all inline code instead of an external file. – Bing Dec 06 '19 at 20:14
  • If a customer wants to shoot themselves in the foot, it's true that it's unclear whether you should give them a gun and take the cash or Do The Right Thing and work out a better solution that will make their lives easier in the long run. [Variable number of variables](https://stackoverflow.com/questions/1373164/how-do-i-create-a-variable-number-of-variables) is relevant here even though it applies to Python. If anyone knows the JS version of this, I'd love to bookmark it and vote dupe. – ggorlen Dec 06 '19 at 20:30
  • Does this answer your question? ["Variable" variables in Javascript?](https://stackoverflow.com/questions/5187530/variable-variables-in-javascript) – ggorlen Dec 06 '19 at 20:39

2 Answers2

1

Assuming you can't do otherwise and everything lives in the global scope, you could do without resorting to using eval(). (I'm pretty sure there must be some valid use cases for eval but I don't think this is one is one of them.)

Since you shouldn't trust the content of these variables you shouldn't be using eval. What if your customer options get compromised? (Or simply your customer doesn't know what they're doing.)

var css_color = 'tweet(document.cookie)';

You can simply leverage the fact that the variables will be accessible from the global scope:

const serialize_options = (...vars) =>
  vars
    .filter(v => typeof this[v] !== 'undefined')
    .map(v => `${v}=${this[v]}`)
    .join('&');
  
console.log(serialize_options('a'));
console.log(serialize_options('a', 'b'));
console.log(serialize_options('a', 'b', 'c'));
console.log(serialize_options('a', 'x', 'y'));
<script>
var a = '10';
var b = '20';
var c = '30';
</script>
customcommander
  • 17,580
  • 5
  • 58
  • 84
  • I'm curious: what's the advantage of `typeof this[v] !== 'undefined'` instead of `this[v] !== undefined`? Beyond that, I'm failing to see how the `var a, var b, var c` approach is better than having the customer inject via an object in the script or JSON string, although, yes, this does answer the question nicely. – ggorlen Dec 06 '19 at 20:44
  • @ggorlen Absolutely none; just force of habit. – customcommander Dec 06 '19 at 20:45
  • 1
    @ggorlen I am with you all; it shouldn't be done like that. But I have also worked in similar environment in the past where requirements are set in stone or you're dealing with legacy systems nobody dares to touch. The "list of options" as JS variables is something I've seen before. Anyway the question was whether `eval` was an ok solution to this problem. I believe I have answered this, explained why it wasn't and provided an alternative. – customcommander Dec 06 '19 at 20:51
  • I agree with all that, thanks. Ideally, OP would state up front that it's a legacy/set-in-stone situation which dispels all of the "don't do it this way" responses. The problem is that in many cases, folks stumble on these threads and without context think this is the right way to do it. – ggorlen Dec 06 '19 at 20:54
  • 1
    @ggorlen Good call, I put a big warning header on top to acknowledge this is a bad requirement to begin with. – Bing Dec 06 '19 at 21:14
0

The answer to this kind of question is almost always "You're thinking about the problem wrong.", as is the case here.

You make the assumption that this should be done with variables and that backs you into the corner of only being able to use eval() to get out of that corner. I understand that the customer may have asked for regular variables, but if I were a house builder and the customer asked me to build a house out of marshmallow, I'd say "no", that's not the way to do it.

The solution is to use an object with keys and values from the start, which allows you to use array indexing syntax later, instead of eval.

// Keep the items you need to check in an object
var obj = {
  a:'foo' // only a gets defined
}


var target = 'exmple.com/?';
var gets = ['a', 'b', 'c']; 

// Check each array element
gets.forEach(function(element) {
  // Objects can use array indexing syntax where a string is passed as the index
  // because objects actually have keys, rather than numeric indexes
  if(obj[element]) { // if it's previously defined
    target += "&" + element + "=" + obj[element]; // add `&a=foo` to target
  }
});

alert(target);
ggorlen
  • 44,755
  • 7
  • 76
  • 106
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71