更新
我提供了一种简洁的解决方案来解决这个问题,其行为类似于Node.js的vm模块。
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]);
}
}
这样做可以直接使用:
var vm = new VM({ prop1: { prop2: 3 } });
console.assert(3 === vm('prop1.prop2'), 'Property access');
这个解决方案仅使用标识符arguments
覆盖命名空间。
感谢Ryan Wheale的想法。
简短版本
使用JavaScript对象作为上下文,评估自定义JavaScript表达式的最佳方法是什么?
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')
它应该能在最新版本的node(0.12)和当时编写时的所有现代浏览器上运行(2015年3月6日)。
注:大多数模板引擎都支持此功能。例如,Jade。
详细说明
我目前正在开发一个应用引擎,其中一个特点是将一段代码与提供的对象一起评估并返回结果。
例如,engine.evaluate('prop1.prop2', {prop1: {prop2: 3}})
应返回3
。
这可以通过使用以下方式轻松完成:
function(code, obj) {
with (obj) {
return eval(code);
}
};
然而,使用with
被认为是不良实践,在ES5严格模式下将无法运行。
在看with
之前,我已经编写了一个替代方案:
function(code, obj) {
return (function() {
return eval(code);
}).call(obj, code);
}
然而,这种方法需要使用
this
。例如:
engine.evaluate('this.prop1.prop2', {prop1: {prop2: 3}})
最终用户不应该使用任何“前缀”。引擎还必须能够评估类似于字符串的内容。
'prop1.prop2 + 5'
并且
'(function() {' +
' console.log(prop1.prop2);' +
' return prop1.prop2;' +
'})()'
并且那些包含对提供对象中函数的调用的代码也需要处理。
因此,它不能仅依赖于将code
字符串拆分为属性名称。
如何解决这个问题?