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));
        }
        // 转换数组的形式[1, 2, 3, [4, 5]]要变为这样的对象:
        // {
        //     chidren: [
        //         {
        //             value: 1
        //         },
        //         {
        //             value: 2
        //         },
        //         {
        //             value: 3
        //         },
        //         {
        //             children: [
        //                 {
        //                     {
        //                         value: 4
        //                     },
        //                     {
        //                         value: 5
        //                     }
        //                 }
        //             ]
        //         }
        //     ]
        // }
 
         function convert(item) {
            if (typeof item == 'number') {
                // 如果传进来的参数是数字
                return {
                    value: item
                };
            } else if (Array.isArray(item)) {
                // 如果传进来的参数是数组
                return {
                    children: item.map(_item => convert(_item))
                };
            }
        }
 

 栈算法

//'智能重复'函数---------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

 

 

posted @ 2021-03-04 14:35  yaqian96  阅读(339)  评论(0)    收藏  举报