AST语法树

AST 简介

在计算机科学中, 抽象语法树(Abstract Syntax Tree, AST)或者简称语法树(Syntax Tree)是源代码语法解构的一种抽象表现, 它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构. – 维基百科

而在 JavaScript 中我们通过 JavaScript Parser 把代码转化为一颗抽象语法树(AST),这颗树定义了代码的结构,通过操纵这颗树,我们可以精准的定位到声明语句、赋值语句、运算语句等等,实现对代码的分析、优化、变更等操作. 然后浏览器会把 js 源码通过解析器转为抽象语法树,再进一步转化为字节码或直接生成机器码.

AST 生成过程

总的来说一段源代码在执行之前会经历如下过程:

  1. 分词 / 词法分析: 将一个语句中的关键词进行提取, 例如let a = 3;, 分词提取之后得到let, a, =, 3, ;
  2. 解析 / 语法分析: 在对上面已经被拆分提取过的关键词进行分析之后建立一课语法树(AST), 效果可参见下面
  3. 底层代码生成: 得到语法树之后执行引擎(例如 chrome 的 v8引擎)会对这颗树进行一定的优化分析, 然后生成更底层的代码或者机器指令交由机器执行

无图不真相, 我们借助一个在线的可视化工具或者esprima来具体看一下过程, 对于如下代码进行生成 AST 树

  1. 源码:

    var a = 42;

    var b = 5;

    function (d) {

    return a + d;

    }

    var c = addA(2) + b;

  2. 词法分析结果

    Keyword(var) Identifier(a) Punctuator(=) Numeric(42) Punctuator(;) Keyword(var) Identifier(b) Punctuator(=)

    Numeric(5) Punctuator(;) Keyword(function) Identifier(addA) Punctuator(() Identifier(d) Punctuator())

    Punctuator({)Keyword(return) Identifier(a) Punctuator(+) Identifier(d)Punctuator(;)

    Punctuator(}) Keyword(var) Identifier(c) Punctuator(=) Identifier(addA)

    Punctuator(()Numeric(2) Punctuator()) Punctuator(+) Identifier(b) Punctuator(;)

  3. 生成 AST 树

    {

    "type": "Program",

    "body": [{

    "type": "VariableDeclaration",

    "declarations": [{

    "type": "VariableDeclarator",

    "id": {

    "type": "Identifier",

    "name": "a"

    },

    "init": {

    "type": "Literal",

    "value": 42,

    "raw": "42"

    }

    }],

    "kind": "var"

    }, {

    "type": "VariableDeclaration",

    "declarations": [{

    "type": "VariableDeclarator",

    "id": {

    "type": "Identifier",

    "name": "b"

    },

    "init": {

    "type": "Literal",

    "value": 5,

    "raw": "5"

    }

    }],

    "kind": "var"

    }, {

    "type": "FunctionDeclaration",

    "id": {

    "type": "Identifier",

    "name": "addA"

    },

    "params": [{

    "type": "Identifier",

    "name": "d"

    }],

    "body": {

    "type": "BlockStatement",

    "body": [{

    "type": "ReturnStatement",

    "argument": {

    "type": "BinaryExpression",

    "operator": "+",

    "left": {

    "type": "Identifier",

    "name": "a"

    },

    "right": {

    "type": "Identifier",

    "name": "d"

    }

    }

    }]

    },

    "generator": false,

    "expression": false,

    "async": false

    }, {

    "type": "VariableDeclaration",

    "declarations": [{

    "type": "VariableDeclarator",

    "id": {

    "type": "Identifier",

    "name": "c"

    },

    "init": {

    "type": "BinaryExpression",

    "operator": "+",

    "left": {

    "type": "CallExpression",

    "callee": {

    "type": "Identifier",

    "name": "addA"

    },

    "arguments": [{

    "type": "Literal",

    "value": 2,

    "raw": "2"

    }]

    },

    "right": {

    "type": "Identifier",

    "name": "b"

    }

    }

    }],

    "kind": "var"

    }],

    "sourceType": "script"

    }

AST的作用

除了帮助执行引擎去生成底层的代码, AST 在我们常见的代码检查工具或者 webpack 中都可以用来作为代码分析的依据, 通过遍历 AST 树, 找出其中的隐藏问题, 或者提出优化的建议, 又或者是代码高亮或者代码压缩都是在分析这颗树的基础上进行的

浏览器渲染过程

首先我们看一下浏览器的深层结构:

  1. 用户界面-包括地址栏, 返回按钮等 UI 组件, 除了主窗口
  2. 浏览器引擎-用来查询和操作渲染引擎的接口
  3. 渲染引擎-负责渲染请求的内容. 比如, 如果请求的资源是html, 那么渲染引擎负责解析 html 和 css, 然后把解析结果渲染到页面中
  4. js 引擎-用来解析执行 JavaScript 代码
  5. 网络连接-用于处理网络请求, 如 http 请求. 这一部分是跨平台的
  6. UI 后台-用于渲染基础组件, 比如多选框和窗口等, 它暴露了一个不是特定平台的通用接口, 在底层调用了操作系统的用户接口
  7. 数据存储-这是一个持久层. 浏览器在硬盘中存储各式数据, 比如 cookie , localStorage 等

各个组件的关系如下:

我们需要注意的是, js 引擎是单线程的, 但是浏览器是多线程的, 比如浏览器会同时开启js 引擎线程, 界面渲染线程, 事件触发线程, http 请求线程

HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。

接下来我们着重看一下渲染引擎所做的工作:

总的过程是: 解析HTML并构建DOM树 => 构建render树 => render树布局 => render树绘制

浏览器引擎开始解析 html, 并把标签转为内容树中的 dom 节点, 同时它也开始解析 css, 外链的 css 以及文件内的 css, 所有这些样式数据以及 html 中的可见性指令都用来构建另外一棵树, – render 树

我们以Safari 和 chrome 使用的Webkit 引擎渲染过程如下:

以上是 AST语法树 的全部内容, 来源链接: utcz.com/a/11116.html

回到顶部