eval5 - 基于 JavaScript 编写的 JavaScript 解释器

2020-03-08 23:00:37 +08:00
 nobo

背景

由于之前项目中为了最求最大灵活度,部分功能预留了 JavaScript 脚本配置入口,如有特殊需求的情况下可不修改代码实现个性化需求。 例如:报表设计中的图形组件,可能不同的报表展示形式多样,此时的界面配置无法提前预设所有可能的情况,如果在图形渲染将前端生成的配置参数交给预先配置好的 JavaScript 脚本进行特殊处理就可以完成个性需求。但该功能需要动态执行 JavaScript 脚本,前端只能通过eval Function方式来动态执行,这种运行方式在浏览器模式下是没问题,但在不支持eval Function的运行环境就无法实现上述需求,例如:微信小程序。

eval5 是完全基于 JavaScript 编写的 JavaScript 解释器,支持完整 ECMA5 语法。

项目地址: https://github.com/bplok20010/eval5

在线体验: https://bplok20010.github.io/eval5

运行原理

步骤一:eval5 通过acorn将源码编译得到树状结构的抽象语法树(AST)

抽象语法树由不同的节点组成,每个节点的 type 标识着不同的语句或表达式,例如: 1+1 的抽象语法树:

{
    "type": "Program",
    "body": [
        {
            "type": "ExpressionStatement",
            "expression": {
                "type": "BinaryExpression",
                "operator": "+",
                "left": {
                    "type": "Literal",
                    "value": 1,
                    "raw": "1"
                },
                "right": {
                    "type": "Literal",
                    "value": 1,
                    "raw": "1"
                }
            }
        }
    ],
    "sourceType": "script"
}

步骤二:为不同的节点 type 编写不同的处理模块并得到最终结果。例如:根据 1+1 的语法树我们可以写出一下解释器代码:

function handleBinaryExpression(node) {
    switch( node.operator ) {
        case '+':
            return node.left.value + node.right.value;
        case '-':
            return node.left.value - node.right.value;
    }
}

生成抽象语法树这一步已经有很多优秀的开源库,如: esprima acorn babylon ...

步骤二执行大概如下:

  1. 先遍历语法树,找出函数声明及变量声明,因为有作用域提升。
  2. 开始执行语句或表达式,建立作用域。(此步骤非常关键)
  3. 根据不同的语法节点type执行对应的处理方法,并得到最后一个表达式返回值。例如BinaryExpression节点则调用handleBinaryExpression方法

更多实现细节就不详细讲解,有兴趣的可直接到 github 上看源码

需要注意的两点:

运行效果

可将 es5 代码直接粘贴到文章开头的体验地址并运行查看效果。以下是运行 echarts 的效果示例:

  1. 复制https://cdn.jsdelivr.net/npm/echarts@4.6.0/dist/echarts.min.js 代码到在线体验中运行。
  2. 复制以下 echart 代码并运行
//设置在线体验容器高度
root.style.height = '300px';
// 基于准备好的 dom,初始化 echarts 实例
var myChart = echarts.init(document.getElementById('root'));
// 指定图表的配置项和数据
var option = {
    title: {
        text: 'ECharts 入门示例'
    },
    tooltip: {},
    legend: {
        data: ['销量']
    },
    xAxis: {
        data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"]
    },
    yAxis: {},
    series: [{
        name: '销量',
        type: 'bar',
        data: [5, 20, 36, 10, 10, 20]
    }]
};
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);      

效果如图:

使用场景

不支持eval Function的运行环境,例如:微信小程序,小程序开发用户可直接看这篇介绍:小程序 eval/Function 终极替代方案:eval5

1638 次点击
所在节点    程序员
0 条回复

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/651042

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX