2

I'm trying to dynamically create node.js variables that point to objects.

I know I can create a variable dynamically in scope using eval:

var vars = ['a','b']
for(var n=0; n<vars.length; n++) {
   eval('var '+vars[n]+' = '+n)
}

console.log(b) // prints 1

The above dynamically creates variables and gives them a value of whatever their index is inside the vars list.

But what if I want to set these dynamic variables to an object reference? Something like the following:

var vars = {a: {}, b:{}}
for(var k in vars) {
   eval('var '+k) // create the variable dynamically
   new Function('value', k+' = value')(vars[k]) // attempt to set the value
}

a.b = 5
console.log(vars.a.b) // undefined : (

I know why the above doesn't work - the function created by new Function can't see the current scope, and so can't set the value. Is there a way to do what I'm trying to do, such that console.log(vars.a.b) would print "5" rather than "undefined"?

UPDATE:

Hmm, I was wrong that new Function can't see or modify variables in the local scope, since this works:

var obj = {}
eval('var x')
new Function('value', 'x = value')(obj)

obj.a = 5
console.log(x.a) // prints 5

So now I'm pretty confused why my loop above doesn't seem to work..

UPDATE 2:

I just realized that my code actually does work in chrome's console. But its not working in node.js...

UPDATE JUST FOR PHIL:

Here's my situation. I'm using Parsimmon to build a parser combinator. This is how that's done:

var L = Parsimmon.createLanguage({
  combinator1: function() {
     return Parsimmon.string('hi')
  },
  combinator2: function() {
     return L.combinator1.many()
  }
}) 

I'd like to eliminate the need to write L. before every parser combinator I write. I could do this:

var combinator2 = L.combinator2

But that would require me to add an additional line like that for every combinator I write. As you can see, I can't use with since L is created after I'd be able to write with(L) and if I define my functions below then use them in the object, I'm back to duplicating those function names every time i write a new combinator.

So to summarize, I'd like to loop through L and put all the generated parser combinators into a nice clean variable in scope so I can write combinator1 instead of L.combinator1 (etc).

15
  • 2
    I dont know the answer .. but just want to know why do you want to do that ? I have never have felt the need to do that :) Commented Sep 27, 2017 at 5:26
  • 1
    the (var[k]) looks suspicious. Should be (vars[k])? Nothing to do with answering your question though. Commented Sep 27, 2017 at 5:28
  • I can't even tell what you're trying to do here. Your function body create a new variable inside its own scope and assigns a value but nothing ever comes of that expression. Did you just want to omit the var keyword inside your function? Commented Sep 27, 2017 at 5:29
  • @nilobarp Yup, typo! Commented Sep 27, 2017 at 5:30
  • 1
    @BT you're welcome :) I mean, if the entire idea is to omit the object prefix, you might as well just wrap everything in with, eg with(objectPrefix) { console.log(prop) }. Can't be any worse than eval Commented Sep 27, 2017 at 5:43

2 Answers 2

1

IIUC, whether or not there are better ways to achieve your goal, if you just remove 'var ' on the dynamic Function, it will operate on the outer (global) scope.

Change:

new Function('value', k+' = value')(vars[k])

to:

new Function('value', k+' = value')(vars[k])

So:

var vars = {a: {}, b:{}}
for(var k in vars) {
   eval('var '+k) // create the variable dynamically
   new Function('value', k+' = value')(vars[k]) // attempt to set the value
}

a.b = 5
console.log(vars.a.b)

You don't want to declare a new variable with local scope inside the function, you want to operate on the outer scope.

Update to address new question

Your first loop does work. Try introspecting on a or b; they are as should be expected, 0 and 1, respectively.

Update 2 based on info this is for Node.js

Per https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function , Function always works on the global scope. In Node, this is on global and var variables are not the global scope but are the module's scope. To fix this for Node, you can do the following and omit your eval var declaration (which was overriding for the module the global scope which you have access to inside Function):

var vars = {a: {}, b:{}}
for(var k in vars) {
   new Function('value', k +' = value')(vars[k]) // attempt to set the value
}

a.b = 5
console.log(vars.a.b) // 5

In other words, the inner function sets variables which automatically become accessible via global, so your module code, in the absence of any module-scoped var declarations of a overwriting the global, you can set the global a properties as with the line a.b = 5.

Update 3

Since I had just been addressing your issue in understanding Function, I gave the above information. As per your follow-up comment (and again, without speaking to its suitability for your specific use case), you can operate on the object via eval as follows:

var vars = {a: {}, b:{}}
for(var k in vars) {
   eval('var '+k+' = vars["'+k+'"]')
}

a.b = 5
console.log(vars.a.b) // 5

But to reiterate the warnings made by others--using eval is generally not a good idea and can be dangerous when the variables referenced include arbitrary user data...

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

11 Comments

Hm, that was actually just a typo. My actual code is identical to what you wrote there. I noticed that this does work in a browser, but not in node.js - which is where I'm trying to do this. I'll add a node.js tag.
Yeah, the first loop works fine in node.js. I get a console log that prints 1. But the problem is in assigning those dynamically created variables an object value that can't be stringified. That second loop that loops through the object keys is what I'd like to have work in node.js
Hey, I'm amazed that worked! Especially after reading the answer here: stackoverflow.com/questions/39416716/… . But thanks!
Just be aware that this is different from your original code in that now it is setting on global--a less safe practice as it sets a for not only this module but for any application code running this module. I am just demonstrating that it is possible to be done this way as Function will work on the global scope. But again, not a good practice typically.
Thanks! I'm the same way : )
|
0

change this line :

from : 
a.b = 5

to
vars.a.b = 5

because a is undefined in your case that's why it is now allowing to create a new property

vars.a will return you a object not only a as a is vars property.

1 Comment

a should have been defined via the eval line

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.