0
var stats = { a:1, b:2, c:3 };
with( stats )
{
    console.log( eval( "(a+b)*2" ) );
};

Greetings, I want to be able to perform an eval only within the scope of an object like above, but without using with. I have seen everywhere that its a major no-no to use with :|

Its just an example and we don't know what expression is in eval nor the object.

Discipol
  • 3,137
  • 4
  • 22
  • 41
  • 1
    What's wrong with `console.log((stats.a + stats.b) * 2)`? – elclanrs Oct 05 '13 at 10:51
  • possible duplicate of [How do you do an eval within the scope of a json?](http://stackoverflow.com/questions/19192513/how-do-you-do-an-eval-within-the-scope-of-a-json) – vkurchatkin Oct 05 '13 at 10:52
  • 4
    using `eval` is a much bigger no-no than using `with`, so when you want to improve your code, you should rather start there. – Philipp Oct 05 '13 at 10:53
  • @elclanrs notice this sentence "Its just an example and we don't know what expression is in eval nor the object." – Discipol Oct 05 '13 at 10:53
  • 1
    @vkurchatkin I think his question is different from the other one. – thefourtheye Oct 05 '13 at 10:53
  • With is definitely a "no-no", but not as much as eval is "evil". It's eval you need to worry about in the first place, not with. – vkurchatkin Oct 05 '13 at 10:54
  • @Discipol: What's the objective here? Why isn't an object lookup a valid approach? It would help if you post a real example of what you're trying to do exactly... – elclanrs Oct 05 '13 at 10:54
  • Because the expression comes from the database and the values inside it are found in that object (which is a sum of many objects with the same properties) – Discipol Oct 05 '13 at 10:57
  • Ha ha ha. I just noticed the title. `combine with and eval but not using with` :P – thefourtheye Oct 05 '13 at 10:57

1 Answers1

5

One possible way:

keys = Object.keys;
values = function(obj) { return keys(obj).map(function(k) {
    return obj[k]
})};

var stats = { a:1, b:2, c:3 };
expr = 'a+b*c'
fun = Function.apply(null, keys(stats).concat('return (' + expr + ')'))
result = fun.apply(null, values(stats))

console.log(result) // 7

The same as a reusable function:

evalWith = function(context, expr) {
    return Function.apply(
        null, Object.keys(context).concat('return (' + expr + ')')
    ).apply(null, Object.keys(context).map(function(k) {
        return context[k]
    }));
}

var stats = { a:1, b:2, c:3 };
result = evalWith(stats, 'a+b*c');

Using eval:

evalWith = function(context, expr) {
    return eval(Object.keys(context).map(function(k) {
        return "var " + k + "= context." + k;
    }).concat(expr).join(";"));
}
georg
  • 211,518
  • 52
  • 313
  • 390
  • 2
    It looks too complex and don't think this is necessary to make simple things hard, IMO. – The Alpha Oct 05 '13 at 11:02
  • But the expression is different `(a+b)*c != a+b*c`. Would it work with parentheses? I think `eval` is acceptable case here. The string is coming from the database not straight user input, it can be sanitized. – elclanrs Oct 05 '13 at 11:05
  • @elclanrs: the `expr` is just a javascript expression. Any js operator will work fine, although some care must be taken if it e.g. contains newlines - see the update. – georg Oct 05 '13 at 11:11
  • @elclanrs I think eval is acceptable as well, but the `with` is the problem. thg435 which part is the with replacement only? Can you update your example to let `eval` be used? – Discipol Oct 05 '13 at 11:11
  • @Discipol This is a replacement for `eval`. – thefourtheye Oct 05 '13 at 11:13
  • But isn't `Function` as a constructor a form of `eval`? I don't see why go through all this trouble TBH... – elclanrs Oct 05 '13 at 11:14
  • @thefourtheye would appreciate a replacement for `with`. – Discipol Oct 05 '13 at 11:15