在没有' eval'的情况下,评估一串JavaScript代码的最简单方法是什么?

时间:2018-06-07 16:05:05

标签: javascript

我创建了一个用于以类似于Vue.js的方式切换CSS类的微库,基于针对某些状态评估的JavaScript字符串。

我有一个包含我要检查的变量的对象,想要覆盖一些基本的表达式,而不必使用一些外部包(我试过jexlexpr-eval但是它们很庞大和善良过时了。

对于非常简单的表达式,没有eval或其他库,是否有任何快速甚至是脏的方法来实现这一目标?

以下是一些例子:

const evaluate = (expr, state) => /* ? */

const state = {
  isActive: true,
  isRed: false,
  count: 637
}

evaluate('isActive', state) // -> true
evaluate('!isRed', state) // -> true
evaluate('isRed == true', state) // -> false
evaluate('isActive && isRed', state) //-> false
evaluate('count > 100', state) // -> true
// ...

2 个答案:

答案 0 :(得分:1)

为什么不使用箭头功能?:

const evaluate = (fn, state) => fn(state);

evaluate(s => s.isActive, state);
evaluate(s => !s.isRed, state);

或者,如果你真的想要评估一个字符串的坏方法:

const evaluate = (expr, state) => {
  with(state) { eval(expr); }
};

答案 1 :(得分:0)

这个不能处理2个值的比较和单个值的评估 - 它也不能处理嵌套对象,但是你说你需要一个非常原始的对象,所以在这里你去:



const matchers = [
        [/^\s*(!?\w+)\s*==\s*(!?\w+)\s*$/,
          function(a, b) { return semieval(a, this) == semieval(b, this) }],
        [/^\s*(!?\w+)\s*===\s*(!?\w+)\s*$/, 
          function(a, b) { return semieval(a, this) === semieval(b, this) }],
        [/^\s*(!?\w+)\s*>=\s*(!?\w+)\s*$/, 
          function(a, b) { return semieval(a, this) >= semieval(b, this) }],
        [/^\s*(!?\w+)\s*<=\s*(!?\w+)\s*$/, 
          function(a, b) { return semieval(a, this) <= semieval(b, this) }],
        [/^\s*(!?\w+)\s*>\s*(!?\w+)\s*$/, 
          function(a, b) { return semieval(a, this) > semieval(b, this) }],
        [/^\s*(!?\w+)\s*<\s*(!?\w+)\s*$/, 
          function(a, b) { return semieval(a, this) < semieval(b, this) }],
        [/^\s*(!?\w+)\s*\|\|\s*(!?\w+)\s*$/, 
          function(a, b) { return semieval(a, this) || semieval(b, this) }],
        [/^\s*(!?\w+)\s*&&\s*(!?\w+)\s*$/, 
          function(a, b) { return semieval(a, this) && semieval(b, this) }],
        [/^\s*!(!?\w+)\s*$/,
          function(a) { return !semieval(a, this) }],
        [/^\s*true\s*$/, 
          function() { return true }],
        [/^\s*false\s*$/, 
          function() { return false }],
        [/^\s*([\d]+)\s*$/, 
          function(a) { return parseInt(a, 10) }],
        [/^\s*(!?\w+)\s*$/, 
          function(a) { return this.hasOwnProperty(a) ? this[a] : a }]
      ];

function semieval(statement, object) {
  for(let matcher of matchers) {
    if(matcher[0].test(statement)) {
      let parts = statement.match(matcher[0]);
      return matcher[1].apply(object, parts.slice(1));
    }
  }
}

const state = {
  isActive: true,
  isRed: false,
  count: 637
}

console.log(
  semieval('isActive', state) // -> true
)
console.log(
  semieval('!isRed', state) // -> true
)
console.log(
  semieval('isRed == true', state) // -> false
)
console.log(
  semieval('!!isRed', state) // -> false
)
console.log(
  semieval('isActive && isRed', state) //-> false
)
console.log(
  semieval('count > 100', state) // -> true
)
&#13;
&#13;
&#13;

这样做是将RegExp的语句与适合以下之一的语句进行比较: a==ba===ba<=ba>=ba<ba>ba&&ba||b,{{ 1}},!a

当找到匹配的模式时,它会尝试找出值a,如果给定a,则首先检查值是b,{{}} {1}},一个数字 - 然后才检查传递的对象是否存在现有密钥并返回其值以进行比较。