微信小程序实现带参数的字符串表达式判断真值
微信小程序实现带参数的字符串表达式判断真值
先看效果图
在这之前有大佬实现了微信小程序中的eval,亲测过,可行,但体量有点大,决定自己写个简单的eval,不求实现复杂逻辑,够用就行。
一、背景
应公司需求,计算表达式的true,false值,如
"STATUS===10||STATUS===20"
计算之前需将其中的
STATUS
变量转变为实际的值如:
"10===10||10===20"
为了更好辨认变量,将变量用特殊字符串标记起来,如:
[%=STATUS%]
,
将原表达式定为
"[%=STATUS%]===10||[%=STATUS%]===20"
二、实现逻辑
将给定表达式
"[%=STATUS%]===10||[%=STATUS%]===20"
分解成数组,同时将标记的变量
STATUS
赋值并去掉标记字符串,得到的字符串如下:
(假设实际
STATUS==='10'
)
['10', '===', '10', '||', '10', '===', '20']
根据数组中的
||
、
&&
拆分成两个简单的数组
['10', '===', '10']
和
'10', '===', '20']
分别硬判断得到两个boolean值,再将两个boolean值做
||
、
&&
操作,得到最终的boolean。
当然这里是两个小表达式组成的情况,如果是三个或以上的情况,就要用到循环算法来解决,这里我采用递归算法。
三、贴上代码
封装类
utils.js
const sfEval = (model, str, property = null) => {
let cs = str.replace(/\s*/g, '').replace(/\[%=/g, '').replace(/%\]/g, '').split(/(&&|\|\||>|<|===|!==|>=|<=|\(|\))/)
for (const cIdx in cs) {
if (property && model[cs[cIdx]] !== undefined) {
// 处理0.aaa的问题
// 处理以0开头的字符串
// parseFloat
// parseFloat('') NaN
// parseFloat(null) NaN
// parseFloat(true) NaN
// parseFloat(false) NaN
// Number('0.aaa') NAN
// 不是''不是null不是true不是false
// 是false // 后面即使是true也不走
// 是 0.aaa true // 后面是false
// 对象和数组都是parseFloat之后都是NaN
if (model[cs[cIdx]]) {
if (!isNaN(parseFloat(model[cs[cIdx]][property])) && !isNaN(Number(model[cs[cIdx]][property]))) {
// 之后一个判断处理字符串'0000'的情况
// 如何处理
if ((model[cs[cIdx]][property] + '').charAt() === '0' && (model[cs[cIdx]][property] + '').length > 1) {
cs[cIdx] = "'" + model[cs[cIdx]][property] + "'"
} else {
cs[cIdx] = parseFloat(model[cs[cIdx]][property])
}
} else {
// 处理下拉框的清除问题 null以及其他null的问题
if (model[cs[cIdx]][property] === null) {
model[cs[cIdx]][property] = ''
}
cs[cIdx] = "'" + model[cs[cIdx]][property] + "'"
}
}
} else if (model[cs[cIdx]] !== undefined) {
// 处理0.aaa的问题
// 处理以0开头的字符串
// parseFloat
// parseFloat('') NaN
// parseFloat(null) NaN
// parseFloat(true) NaN
// parseFloat(false) NaN
// Number('0.aaa') NAN
// 不是''不是null不是true不是false
// 是false // 后面即使是true也不走
// 是 0.aaa true // 后面是false
// 对象和数组都是parseFloat之后都是NaN
if (!isNaN(parseFloat(model[cs[cIdx]])) && !isNaN(Number(model[cs[cIdx]]))) {
// 之后一个判断处理字符串'0000'的情况
// 如何处理
if ((model[cs[cIdx]] + '').charAt() === '0' && (model[cs[cIdx]] + '').length > 1) {
cs[cIdx] = "'" + model[cs[cIdx]] + "'"
} else {
cs[cIdx] = parseFloat(model[cs[cIdx]])
}
} else {
// 处理下拉框的清除问题 null以及其他null的问题
if (model[cs[cIdx]] === null) {
model[cs[cIdx]] = ''
}
cs[cIdx] = "'" + model[cs[cIdx]] + "'"
}
} else if ((cs[cIdx].indexOf("'") < 0 || (!isNaN(parseFloat(cs[cIdx].replace(/'/g, ''))) && !isNaN(Number(cs[cIdx].replace(/'/g, ''))))) && !cs[cIdx].match(/^(&&|\|\||>|<|===|!==|>=|<=|\(|\))$/)) {
// convert to string fix bug for model is null with FIELD = '0', FIELD is undefined
if (!isNaN(parseFloat(cs[cIdx].replace(/'/g, ''))) && !isNaN(Number(cs[cIdx].replace(/'/g, '')))) {
if ((cs[cIdx] + '').charAt() === '0' && (cs[cIdx] + '').length > 1) {
cs[cIdx] = "'" + cs[cIdx] + "'"
} else {
cs[cIdx] = parseFloat(cs[cIdx].replace(/'/g, ''))
}
} else {
cs[cIdx] = "'" + cs[cIdx] + "'"
}
}
}
let csTemp = JSON.parse(JSON.stringify(cs))
return true && fn(csTemp)
}
const fn = (csTemp) => {
let resultBool = false
let arr1 = []
let arr2 = []
if (csTemp.length < 4) {
resultBool = calcFuc(csTemp)
return resultBool
} else {
for (let i in csTemp) {
if (csTemp[i] == '&&' || csTemp[i] == '||') {
arr1 = csTemp.slice(0, i)
arr2 = csTemp.slice(Number(i) + 1, Number(i) + 4)
if (csTemp[i] == '&&') {
resultBool = calcFuc(arr1) && calcFuc(arr2)
} else {
resultBool = calcFuc(arr1) || calcFuc(arr2)
}
csTemp.splice(0, Number(i) + 4, resultBool)
return fn(csTemp)
break
}
}
}
}
const calcFuc = (arr) => {
for (let i = 0; i < arr.length; i++) {
switch (arr[i]) {
case '&&':
return arr[i - 1] && arr[i + 1]
case '||':
return arr[i - 1] || arr[i + 1]
case '>':
return arr[i - 1] > arr[i + 1]
case '<':
return arr[i - 1] < arr[i + 1]
case '===':
return arr[i - 1] === arr[i + 1]
case '!==':
return arr[i - 1] !== arr[i + 1]
case '>=':
return arr[i - 1] >= arr[i + 1]
case '<=':
return arr[i - 1] <= arr[i + 1]
case true:
return true
default:
break
}
}
return false
}
module.exports = {
sfEval
}
四、调用方法
先import封装方法,然后调用
let model = {
USER: 'a',
STATUS: 0,
ROLE: 2
}
let str = "[%=STATUS%]==='0'&&[%=ROLE%]==='2'&&[%=USER%]==='a'"
console.log('sfEval', sfEval(model, str))
五、运行结果
六、注意事项
本篇文章实现逻辑中包含的eval可仅跑通类似以下形式的字符串:
"2===2&&3!==4"
"2===2&&3!==4&&'a'!=='b'"
"2===2&&3!==4&&'a'!=='b'||'a'!=='b'"
"2===2||3!==4&&'a'!=='b'||'a'!=='b'"
不支持括号,按照从左到右的顺序计算。
七、结束语
到此就结束了,希望能帮到各位,记得拿走时请扣6
有问题在评论区留言