JavaScript不使用eval/Function构建遵循优先级的布尔表达式求值
问题答案
完全不需要使用eval或Function,可以通过遵循JavaScript运算符优先级规则的原生逻辑实现安全求值,不存在代码注入风险。
实现原理
JavaScript中逻辑运算符的优先级规则为:&&(逻辑与)优先级高于||(逻辑或)。因此仅包含这两种运算符的布尔表达式,求值顺序可以拆为两步:
- 优先计算所有被
&&连接的连续布尔值片段,得到每个片段的计算结果 - 将所有片段的结果通过
||连接计算,得到最终值
以题目给出的表达式true && true || false && false为例:
- 第一步拆分
&&片段计算:true && true结果为true,false && false结果为false - 第二步计算或运算:
true || false结果为true,和JS原生求值结果完全一致
直接从左到右遍历计算会得到错误结果,本质是提前计算了||之后的&&运算,违反了优先级规则。
具体实现代码
/** * 安全计算仅包含&&、||的布尔表达式,无需eval/Function * @param {boolean[]} values 布尔值数组 * @param {('&&'|'||')[]} ops 逻辑运算符数组,长度需比values少1 * @returns {boolean} 表达式最终求值结果 */ function safeEvalBoolean(values, ops) { // 入组合法性校验 if (values.length !== ops.length + 1) { throw new Error('参数不合法:布尔值数组长度必须比运算符数组长1'); } // 第一步:优先计算所有高优先级的&&运算,按||拆分求值片段 const orGroups = [values[0]]; for (let i = 0; i < ops.length; i++) { const op = ops[i]; const nextVal = values[i + 1]; switch(op) { case '&&': // &&优先级高,直接和当前分组最后一个值运算后替换,保留短路特性 orGroups[orGroups.length - 1] = orGroups.at(-1) && nextVal; break; case '||': // ||优先级低,将下一个值作为新分组存入 orGroups.push(nextVal); break; default: throw new Error(`不支持的运算符:${op}`); } } // 第二步:所有分组结果做||运算,利用some短路特性提升性能 return orGroups.some(item => item); }
测试验证
const to_eval = [true, true, false, false]; const ops = ['&&', '||', '&&']; console.log(safeEvalBoolean(to_eval, ops)); // 输出true,符合JS原生求值结果
扩展说明
该实现完全保留了JS逻辑运算符的短路特性,和原生表达式执行行为完全一致。如果后续需要支持更多运算符(比如逻辑非!、比较运算符、括号等),可以使用调度场算法先将中缀表达式转换为后缀表达式(逆波兰表达式),再通过栈结构求值,整个过程依然是纯数值运算,不需要动态执行代码,同样没有安全风险。对于仅包含&&和||的场景,上述两步法代码最简、性能最高。
内容的提问来源于stack exchange,提问作者Mustafamond77
相关产品推荐
相关产品推荐

