Update
I've come up with a concise solution to this problem, that behaves similar to node's vm module.
var VM = function(o) {
eval((function() {
var src = '';
for (var prop in o) {
if (o.hasOwnProperty(prop)) {
src += 'var ' + prop + '=o[\'' + prop + '\'];';
}
}
return src;
})());
return function() {
return eval(arguments[0]);
}
}
This can then be used as such:
var vm = new VM({ prop1: { prop2: 3 } });
console.assert(3 === vm('prop1.prop2'), 'Property access');
This solution overrides the namespace with only the identifier arguments
taken.
Thanks to Ryan Wheale for his idea.
Short version
What is the best way to evaluate custom javascript expression using javascript object as a context?
var context = { prop1: { prop2: 3 } }
console.assert(3 === evaluate('prop1.prop2', context), 'Simple expression')
console.assert(3 === evaluate('(function() {' +
' console.log(prop1.prop2);' +
' return prop1.prop2;' +
'})()', context), 'Complex expression')
It should run on the latest version of node (0.12) and all evergreen browsers at the time of writing (3/6/2015).
Note: Most templating engines support this functionality. For example, Jade.
Long version
I'm currently working on an application engine, and one of its features is that it takes a piece of code and evaluates it with a provided object and returns the result.
For example, engine.evaluate('prop1.prop2', {prop1: {prop2: 3}})
should return 3
.
This can be easily accomplished by using:
function(code, obj) {
with (obj) {
return eval(code);
}
};
However, the usage of with
is known to be bad practice and will not run in ES5 strict mode.
Before looking at with
, I had already written up an alternative solution:
function(code, obj) {
return (function() {
return eval(code);
}).call(obj, code);
}
However, this method requires the usage of this
.
As in: engine.evaluate('this.prop1.prop2', {prop1: {prop2: 3}})
The end user should not use any "prefix".
The engine must also be able to evaluate strings like
'prop1.prop2 + 5'
and
'(function() {' +
' console.log(prop1.prop2);' +
' return prop1.prop2;' +
'})()'
and those containing calls to functions from the provided object.
Thus, it cannot rely on splitting the code
string into property names alone.
What is the best solution to this problem?