【问题标题】:Parse a string into conditions in javaScript?将字符串解析为javascript中的条件?
【发布时间】:2017-04-04 09:23:11
【问题描述】:

我有一个包含一些条件的字符串,例如:

var str = "this.demoModel.active == '1' && this.demoModel.span > 5 || ..."

javascript 中是否有直接的方法来解析它们,以便它们像一组条件一样工作。类似的东西:

if (JSON.parse(str) {})。 ??

【问题讨论】:

  • @musefan 不鼓励使用eval
  • 重新思考你的逻辑。将代码存储在字符串中以便稍后对其进行评估是不好的做法。
  • @SagarV:我不需要你向我解释危险。我希望你支持你的陈述。有完全有效的情况可以正确使用,所以盲目地说“不要使用它”是不对的。你甚至不知道 OP 的全部情况,所以你怎么能确定它是错误的?
  • @AshishRanjan 这些字符串来自哪里?为什么需要评估字符串而不是运行真实代码?
  • @musefan 因为他没有足够的经验,甚至不知道evalFunction 构造函数,所以你如何假设他会知道使用它们的可能含义/危险?这就是为什么他应该不鼓励使用它们,而是寻找一种不同的方法来解决他的问题。为什么eval 通常不受欢迎是因为在社区拥有数十年的经验。

标签: javascript parsing


【解决方案1】:

一般来说,您应该尽量避免陷入这种情况:如果可能,应该避免将 JavaScript 存储在字符串中以供以后评估。根据您的实际情况,您可以考虑以下选项:

1。使用模板文字:

它们在实际使用中受到限制,因为它们与使用它们的脚本一起解析,但they are also safe

var str =  `${this.demoModel.active == '1' && this.demoModel.span > 5}`;

分配此字符串后,它将立即评估其中的${ } 部分。

因此,如果评估可以立即发生,这只是一种解决方案,因为您不能将其存储在字符串中,然后期望稍后触发评估。

所以它与:

var bool = this.demoModel.active == '1' && this.demoModel.span > 5;

2。通过回调延迟评估

解决方法可能是定义一个评估模板文字或表达式的函数,如下所示:

var rule = function() { 
    return this.demoModel.active == '1' && this.demoModel.span > 5;
};

...您可以传递该函数,例如作为回调:

doSomething(rule);

...然后 doSomething 可以这样调用它,绑定上下文,以便 this 具有适当的值:

function doSomething(rule) {
    if (rule.call(this)) console.log('rule passed');
}

3。嵌套对象数据结构

另一种选择是为表达式创建一个对象结构,例如:

var rules = [
    [{ // AND conditions:
        "field": "active",
        "compare": "eq",
        "target": 1
    }, {
        "field": "span",
        "compare": "gt",
        "target": 5
    }], // Next array will be OR'ed
    [{
        "field": "...."
        "compare": "..",
        "target": ...
    }]
}];

这是一个嵌套数组,其中顶层的规则必须被 OR'ed 在一起,而内层将被 AND'ed 在一起。

然后编写一个函数来处理这个结构。 compare 名称可以映射到代码中的函数,如下所示:

const comparators = {
    "eq": (a, b) = a === b,
    "gt": (a, b) = a > b
};

所以要评估 rules 数组中的一个对象,你可以使用这个:

execute: (obj) => comparators[this.demoModel[obj.compare]] // get the function
    (this.demoModel[obj.field], obj.target) // pass arguments

这个 rules 结构可以很容易地保存和加载为 JSON 字符串。

【讨论】:

  • 我真的认为Template literals 是正确的解决方案。但在我的情况下,模型的值可能会在一段时间后发生变化,并且根据我在尝试后观察到的情况,模板文字只被评估一次,即使数据发生了变化,也会得出相同的旧评估结果。 JSON 实现可能会变得复杂,但我会尝试一下。谢谢:)
  • 确实,就像我写的那样,模板文字在遇到它们时会立即被评估和解析。对于延迟执行,您需要另一种解决方案。 JSON 非常灵活,您只需定义允许发生的事情,以及不允许发生的事情。这样你就可以确保你的代码永远不会执行你真正不想要的东西。
  • 只是事后诸葛亮。如果你的函数中有模板文字,你总是可以再次执行该函数,然后模板文字也将被再次评估。
  • 模板字面量存在于不同的类 A 中,该类将字符串传递给不同类 B 的函数(评估应在 B 类中完成) 注意:该函数可能有多个调用者类在 B 类中。
  • 感谢您提出第 3 点建议,我采用了该解决方案,最终它的工作量并不比纯 eval 多多少,但它显着提高了可读性和安全性
【解决方案2】:

有,但使用它们通常是最后的手段:evalFunction 构造函数。 eval 在调用 eval 的上下文中从字符串输入运行代码。 Function 构造函数从字符串创建一个函数。自然,在这两种情况下,这意味着您必须信任字符串的来源,因为它可以包含任意代码,并且您很乐意在代码的上下文中运行它。因此,例如,您不会从用户 A 获取一个字符串,然后在用户 B 的浏览器中运行它——这会使用户 B 对用户 A 的攻击敞开大门。(对于“浏览器”替换“会话”或其他任何内容,问题在 Node 等环境中的服务器端和在浏览器中的客户端一样真实——也许更是如此。)

如果您接受来自用户的字符串并在他们自己的浏览器/会话/上下文中运行它,那很好。

这是一个例子:

function Foo() {
  this.demoModel = {
    active: "1",
    span: 7
  };
}
Foo.prototype.run = function(str) {
  if (eval(str)) {
    console.log("Yes");
  } else {
    console.log("No");
  }
};
var f = new Foo();
f.run("this.demoModel.active == '1' && this.demoModel.span > 5");
f.run("this.demoModel.active == '0' && this.demoModel.span < 5");

你也可以使用Function构造函数,然后用正确的this调用它:

function Foo() {
  this.demoModel = {
    active: "1",
    span: 7
  };
}
Foo.prototype.run = function(str) {
  var testfunc = new Function("return " + str);
  if (testfunc.call(this)) {
    console.log("Yes");
  } else {
    console.log("No");
  }
};
var f = new Foo();
f.run("this.demoModel.active == '1' && this.demoModel.span > 5");
f.run("this.demoModel.active == '0' && this.demoModel.span < 5");

如果您必须这样做,请尽可能使用Function 构造函数而不是eval,因为它无法访问您使用它的范围内的所有内容,但它们都是您需要警惕的强大工具。

【讨论】:

  • 感谢您的精彩解释!如果我真的需要这个,我需要重新考虑。根据您的解释,我认为在我的情况下使用 eval 或函数构造函数是可以的。我正在使用 angular 2 并制作一个循环运行的服务,并在调用者传递的条件满足时停止。我在this 上下文中遇到了问题,但我会弄清楚并在给出最终实现之前尝试了解更多后果。
【解决方案3】:

您可以使用 'eval' 或 'Function' 但正如 on MDN 所述

不要不必要地使用 eval ! - eval() 是一个危险的函数,它以调用者的权限执行它传递的代码。如果您使用可能受到恶意方影响的字符串运行 eval(),您最终可能会在具有您网页/扩展程序权限的用户计算机上运行恶意代码。更重要的是,第三方代码可以看到 eval() 被调用的范围,这可能导致类似 Function 不易受到的攻击。

if(new Function("CONDITON_STRING")()){
 //Answer
};

【讨论】:

    【解决方案4】:

    尝试以下方法:

    var str =  'true && true';
    var str2 =  'true && false';
    function strEval(fn) {
      return new Function('return ' + fn)();
    }
    var conditionTrue = strEval(str);
    var conditionFalse = strEval(str2)
    if(conditionTrue)
    {
        console.log(conditionTrue)
    }
    if(!conditionFalse)
    {
        console.log(conditionFalse)
    }

    【讨论】:

      【解决方案5】:

      使用 eval 方法将字符串转换为命令。

      var cond = 'd === 1';
      var d = 0;
      
      if (eval(cond)) { console.log('1'); } else { console.log('2'); }    // output: 2
      d = 1;
      if (eval(cond)) { console.log('1'); } else { console.log('2'); }    // output: 1
      

      【讨论】:

      • 建议 eval 而不警告其危险是一个坏主意™。
      猜你喜欢
      • 2020-05-15
      • 2018-03-08
      • 1970-01-01
      • 2012-05-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-10
      相关资源
      最近更新 更多