vue源码之AST抽象语法树
1.定义
抽象语法树:将模板语法转换成正常的HTML语法中间的方法 (Abstract Syntax Tree)
将源码转换成一种特定形式的表达,它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。
抽象语法树其实就是将一类标签转化成通用标识符,从而结构出的一个类似于树形结构的语法树
用途:编译器、浏览器
使用的场景:
- JS 反编译,语法解析
- Babel 编译 ES6 语法
- 代码高亮
- 关键字匹配
- 作用域判断
- 代码压缩
2.本质
js对象,其中有attrs,tag,type(节点属性),children(子元素)等属性
下列只是抽象语法树的一种举例----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3.抽象语法树和虚拟节点的关系
模板语法-----》抽象语法树-----》h函数----》虚拟DOM-----》diff算法----》界面
4.算法之指针
--------- 寻找连续重复次数最多的字符
指针就是下标位置。
//如果i和j指向的字一样,则i不动,j后移 //如果指向的字不一样,说明他们之间的字是连续相同的,让i==j,j后移动 var str='aaabbbbcccccdddd' var i=0; var j=1; let maxCount=0; let maxChar=' while(i<=str.length-1){ if(str[i]!==str[j]){ console.log("i和j之间的重复次数j-i次") if (j-i > maxCount){ maxCount=j-i; maxCount=str[i]; } i=j; } j++; }
递归算法
// 试输出斐波那契数列的前10项,即1、1、2、3、5、8、13、21、34、55 // 缓存对象 var cache = {}; // 创建一个函数,功能是返回下标为n的这项的数字 function fib(n) { // 判断缓存对象中有没有这个值,如果有,直接用 if (cache.hasOwnProperty(n)) { return cache[n]; } // 缓存对象没有这个值 // 看下标n是不是0或者是不是1,如果是,就返回常数1 // 如果不是,就递归 var v = n == 0 || n == 1 ? 1 : fib(n - 1) + fib(n - 2); // 写入缓存。也就是说,每算一个值,就要把这个值存入缓存对象。 cache[n] = v; return v; } for (let i = 0; i <= 9; i++) { console.log(fib(i)); }
栈算法
//'智能重复'函数---------3[2[ad]2[c]] adadccadadccadadcc //利用两个栈来存储,一个存储数字,一个存储字母 //当指针移动时,碰到数字,则进栈1,指针后移数字的长度加1,栈2推入空字符串 //当碰到是字符串时,则将字母入栈,指针后移字符串的长度。 //当碰到]时,栈1和栈2进行弹栈,栈2的字符串重复栈1弹出的数字的次数,并拼接到栈2的栈顶的位置。 function smartRepeat(templater){ var index=0; var stack1=[]; //存放数字 var stack2=[]; //存放字母 var rest=templater.length; while(index<templater.length-1){ rest=templater.substring(index); if(/^\d+\[/.test(rest)){ let times = Number(rest.match(/^(\d+)\[/)[1]); stack1.push(times); stack2.push(''); index += times.toString().length + 1; }else if(/^\w+\]/.test(rest)){ let word = rest.match(/^(\w+)\]/)[1]; stack2[stack2.length - 1] = word; index += word.length; }else if(rest[0] == ']'){ let times = stack1.pop(); let word = stack2.pop(); stack2[stack2.length - 1] += word.repeat(times); index++; } } return stack2[0].repeat(stack1[0]); }
AST抽象语法树:实现原理运用栈和递归算法。
思想:运用上述的栈算法,开始标记为<div>等,结束标记为</div> ,栈1是个辅助作用,栈1出栈时,将出栈1的标签和栈2出栈的元素,合成children,添加到栈2最后一项的children上。
attrs的操作:
将上图转换成["class="aa bb cc","id=“mybox""]
// 当前是否在引号内 var isYinhao = false // 断点 var point = 0; // 结果数组 var result = []; for (let i = 0; i < attrsString.length; i++) { let char = attrsString[i]; if (char == '"') { isYinhao = !isYinhao; } else if (char == ' ' && !isYinhao) { // 遇见了空格,并且不在引号中 console.log(i); if (!/^\s*$/.test(attrsString.substring(point, i))) { result.push(attrsString.substring(point, i).trim()); point = i; } } } // 循环结束之后,最后还剩一个属性k="v" result.push(attrsString.substring(point).trim());
5.AST的形成(js中的)
js 执行的第一步是读取 js 文件中的字符流,然后通过词法分析生成 token
,之后再通过语法分析( Parser )生成 AST,最后生成机器码执行。
整个解析过程主要分为以下两个步骤:
- 分词:将整个代码字符串分割成最小语法单元数组----------生成tokens列表,每个字符都是一个token
- 语法分析:在分词基础上建立分析语法单元之间的关系-----------将token转换成AST
JS Parser 是 js 语法解析器,它可以将 js 源码转成 AST,常见的 Parser 有 esprima、traceur、acorn、shift 等。
定义以及属性,参考:http://www.goyth.com/2018/12/23/AST/#Expressions