V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
nobo
V2EX  ›  程序员

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

  •  1
     
  •   nobo · 2020-03-08 23:00:37 +08:00 · 1637 次点击
    这是一个创建于 1754 天前的主题,其中的信息可能已经有所发展或是发生改变。

    背景

    由于之前项目中为了最求最大灵活度,部分功能预留了 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;
        }
    }
    

    运行原理.png

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

    步骤二执行大概如下:

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

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

    需要注意的两点:

    • 作用域处理
    • 函数创建及调用

    运行效果

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

    1. 复制https://cdn.jsdelivr.net/npm/[email protected]/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);      
    

    效果如图: 效果.png

    使用场景

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

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3456 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 04:58 · PVG 12:58 · LAX 20:58 · JFK 23:58
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.