从阵列中构建布尔表达,并在JavaScript中一次评估整个表达

发布于 2025-02-11 07:46:09 字数 427 浏览 3 评论 0原文

我有两个数组 - 一个包含布尔值,另一个运算符:

to_eval = [true, true, false, false]
ops=['&&', '||', '&&']

我想构建一个表达式,

result = true && true || false && false

如果我迭代阵列在我走时评估的阵列,则应根据优先规则进行评估

,结果是false 。

我可以构建一个字符串并使用评估或功能 - 但是根据我一直在阅读的内容,可能会在Web应用程序中引起安全问题。

在JavaScript中,是否有一种方法可以在不评估它的情况下构建表达式,直到完全形成为止,以便在不使用ERAT或功能的情况下观察优先规则?

I have two arrays - one that contains booleans, and the other operators:

to_eval = [true, true, false, false]
ops=['&&', '||', '&&']

Out of this I'd like to build an expression

result = true && true || false && false

Which should evaluate to true based on precedence rules

If I iterate over the arrays evaluating as I go, the result is false.

I could build a string and use Eval or Function - but from what I have been reading this can cause security issues in a web application.

Is there a way in Javascript to build out an expression without evaluating it till it is fully formed so that precedence rules are observed without using Eval or Function?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

随心而道 2025-02-18 07:46:09

如果确定您的两个阵列具有预期值(布尔值和预期运算符),则可以安全地调用est。因此,只需添加一些代码即可验证两个给定的输入。

您可以按照以下操作:

function evaluate(bools, ops) {
    // Verify the given arguments are as expected
    if (!ops.every(op => ["&&", "||"].includes(op))) throw "invalid operator";
    if (!bools.every(bool => typeof bool === "boolean")) throw "invalid operand";
    if (bools.length !== ops.length + 1) throw "length mismatch";
    return eval(bools.map((bool, i) => bool + " " + (ops[i] ?? "")).join(" "));
}

let to_eval = [true, true, false, false];
let ops = ['&&', '||', '&&'];
let result = evaluate(to_eval, ops);
console.log(result);

You can safely call eval, if it is certain that your two arrays have the expected values (booleans and expected operators). So just add some code to verify the two given inputs.

You can do as follows:

function evaluate(bools, ops) {
    // Verify the given arguments are as expected
    if (!ops.every(op => ["&&", "||"].includes(op))) throw "invalid operator";
    if (!bools.every(bool => typeof bool === "boolean")) throw "invalid operand";
    if (bools.length !== ops.length + 1) throw "length mismatch";
    return eval(bools.map((bool, i) => bool + " " + (ops[i] ?? "")).join(" "));
}

let to_eval = [true, true, false, false];
let ops = ['&&', '||', '&&'];
let result = evaluate(to_eval, ops);
console.log(result);

瘫痪情歌 2025-02-18 07:46:09

您可以使用 数组#redard

但是,这种方式从左到右评估而没有先例,因此true&& true || false&& false将评估false。如果您想要更准确的结果,例如JavaScript解析事物的方式,则使用适当的消毒使用est更容易。

const to_eval = [true, true, false, false];
const ops = ['&&', '||', '&&'];

const ops_map = {
  '&&': (a, b) => a && b,
  '||': (a, b) => a || b
};

const result = to_eval.reduce((acc, cur, i) => ops_map[ops[i-1]](acc, cur));

console.log(result);

有了适当的秘密化,您可以安全地使用eval

const evaluate = (to_eval, ops = []) => {
  const expression = to_eval.reduce((acc, cur, i) => {
    let left = i === 1 ? !!acc : acc;
    let op = {'&&': '&&', '||': '||'}[ops[i-1]];
    if(typeof op !== 'string') {
      op = '||';  // fallback
    }
    let right = !!cur;
    
    return `${left} ${op} ${right}`
  });
  const result = eval(expression);
  console.log(expression, ' => ', result);
  return result;
}

const to_eval = [true, true, false, false]
const ops = ['&&', '||', '&&'];
evaluate(to_eval, ops);

evaluate([true], []);

evaluate(['alert(1)', 0, false], ['alert(2)', 'hasOwnProperty']);

You can create a mapping functions for the trusted logic operators safely with Array#reduce:

But the way this evaluates from left to right without precedences, so true && true || false && false will evaludate false. If you want more accurate result like how JavaScript parse things, it would be easier to use eval with proper sanitization.

const to_eval = [true, true, false, false];
const ops = ['&&', '||', '&&'];

const ops_map = {
  '&&': (a, b) => a && b,
  '||': (a, b) => a || b
};

const result = to_eval.reduce((acc, cur, i) => ops_map[ops[i-1]](acc, cur));

console.log(result);

With proper santization, you can safely use eval:

const evaluate = (to_eval, ops = []) => {
  const expression = to_eval.reduce((acc, cur, i) => {
    let left = i === 1 ? !!acc : acc;
    let op = {'&&': '&&', '||': '||'}[ops[i-1]];
    if(typeof op !== 'string') {
      op = '||';  // fallback
    }
    let right = !!cur;
    
    return `${left} ${op} ${right}`
  });
  const result = eval(expression);
  console.log(expression, ' => ', result);
  return result;
}

const to_eval = [true, true, false, false]
const ops = ['&&', '||', '&&'];
evaluate(to_eval, ops);

evaluate([true], []);

evaluate(['alert(1)', 0, false], ['alert(2)', 'hasOwnProperty']);

冷夜 2025-02-18 07:46:09

如果您对输入进行消毒(即,验证所有令牌是否来自非常具体的令牌列表),则可以使用评估。

function safeEval(expression) {
    const allowed = ['true', 'false', '&&', '||'];
    if (expression.split(/\s+/).every(token => allowed.includes(token))) {
        return eval(expression);
    } else {
        throw Error('Encountered forbidden token.');
    }
}

console.log(safeEval('true && true || false && false'));
console.log(safeEval('undefined'));

这远非您可以编写的最有效的代码,但是它很简单并且完成工作,并且对其他操作员和括号添加支持是微不足道的。

另外,您可以根据自己想要的优先顺序自己评估操作员:

const expression = [true, '&&', true, '||', false, '&&', false];

for (let i = 0; i < expression.length; ++i) {
    if (expression[i] === '&&') {
        const left = expression[i - 1];
        const right = expression[i + 1];
        expression.splice(i - 1, 3, left && right);
    }
}

console.log(expression);

for (let i = 0; i < expression.length; ++i) {
    if (expression[i] === '||') {
        const left = expression[i - 1];
        const right = expression[i + 1];
        expression.splice(i - 1, 3, left || right);
    }
}

console.log(expression);

如果您要增加许多操作员的支持,您可能希望使此代码更加健壮,重复性降低,但这至少应该使您启动。

You could use eval if you sanitize the input (IE, verify that all the tokens are from a very specific list of allowed tokens).

function safeEval(expression) {
    const allowed = ['true', 'false', '&&', '||'];
    if (expression.split(/\s+/).every(token => allowed.includes(token))) {
        return eval(expression);
    } else {
        throw Error('Encountered forbidden token.');
    }
}

console.log(safeEval('true && true || false && false'));
console.log(safeEval('undefined'));

This is far from the most efficient code you could write, but it's simple and gets the job done, and adding support for other operators and parentheses is trivial.

Alternatively, you could evaluate the operators yourself, in the order of precedence you desire:

const expression = [true, '&&', true, '||', false, '&&', false];

for (let i = 0; i < expression.length; ++i) {
    if (expression[i] === '&&') {
        const left = expression[i - 1];
        const right = expression[i + 1];
        expression.splice(i - 1, 3, left && right);
    }
}

console.log(expression);

for (let i = 0; i < expression.length; ++i) {
    if (expression[i] === '||') {
        const left = expression[i - 1];
        const right = expression[i + 1];
        expression.splice(i - 1, 3, left || right);
    }
}

console.log(expression);

You probably would want to make this code more robust and less repetitive if you were to add support for many operators, but that should get you started at least.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文