基于Array的JS表達式執行函數實現打賞

最近項目涉及到表達式,簡單封裝了一些功能,如表達式解析、執行等,解析不用說,網上很多表達式字符串解析為詞法樹的庫,我們也不必再造輪子(我用jsep進行解析),這里說下執行表達式的方式。

默認字符串解析抽象詞法樹按理說是可以直接解析執行的,只不過通常此類詞法樹要表達的內容過于多,數據的存儲也過于龐大,于是我們結合自己業務對關鍵數據做了抽象,比如

// 字符串
a && b
// 解析完
{
    type: "LogicalExpression",
    operator: "&&",
    left: {type: "Identifier", name: "a"},
    right: {type: "Identifier", name: "b"}
}
// 而我們需要的關鍵信息就是:表達式包含a、b并且他們兩個中間是&&,于是改造一下
['&&', 'a', 'b']
//是不是簡潔多了

下面實現個表達式執行函數的map枚舉

const rulesMap = {
    '===': (left, right) => left === right,
    '!==': (left, right) => left !== right,
    '<': (left, right) => left < right,
    '<=': (left, right) => left <= right,
    '>': (left, right) => left > right,
    '>=': (left, right) => left >= right,
    '&&': (left, right) => !!(left && right),
    '||': (left, right) => !!(left || right),
    '!': value => !value
}
// 如果不想直接按程序語言,key完全可以根據愛好來

既然是我們自己定義的表達式,就可以實現一個校驗

function isValidCondition(expression) {
    return Array.isArray(expression) && !!rulesMap[expression[0]]
}

表達式可能存在變量,支持一下業務數據的上下文替換(我們變量以@開頭)

const get = require('lodash/get')
const rVariable = /^@(.+)/

function replaceVariable(variable, context) {
    if (context && typeof variable === 'string' && rVariable.test(variable)) {
        const key = variable.substr(1)
        if (Array.isArray(context)) {
            for (let i = 0; i < context.length; i++) {
                const value = get(context[i], key)
                if (value !== undefined) {
                    return value
                }
            }
        } else {
            return get(context, key)
        }
    }
    return variable
}

最后就是執行入口

function callExpression(type, args) {
    if (rulesMap[type]) {
        return rulesMap[type](...args)
    }
    throw new Error(`no support expression type \`${type}\``)
}

是不是缺點什么?對咯,表達式可能嵌套

function canBreak(type, value) {
    return (type === '||' && value) || (type === '&&' && !value)
}

function exec(expression, context) {
    if (isValidCondition(expression)) {
        const value = []
        const type = expression[0]
        for (let i = 1; i < expression.length; i++) {
            if (isValidCondition(expression[i])) {
                value.push(exec(expression[i], context))
            } else {
                value.push(replaceVariable(expression[i], context))
            }
            if (value[0] !== undefined && canBreak(type, value[0])) {
                value.push(value[0])
                break
            }
        }
        return callExpression(type, value)
    }
    return false
}

前面幾個函數都用上了,簡單高效(不直接存儲a && b,而是存儲['&&','a','b'],達到運行時不用解析字符串的目的,從而在執行層面提高效率)的表達式運行函數編寫完成。

基于Array的JS表達式執行函數實現
文章《基于Array的JS表達式執行函數實現》二維碼
  • 微信打賞
  • 支付寶打賞

暫無評論

(必填)

(必填)

(可選)

黑龙江22选5开奖