前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >抽象语法树

抽象语法树

原创
作者头像
花落花相惜
发布2021-12-06 13:25:03
2.5K0
发布2021-12-06 13:25:03
举报
文章被收录于专栏:花落的技术专栏

栗子:

代码语言:txt
复制
  <div class="box">
代码语言:txt
复制
    <h3 class="title">我是一个标题</h3>
代码语言:txt
复制
    <ul>
代码语言:txt
复制
      <li v-for="(item, index) in arr" :key="index">{{item}}</li>
代码语言:txt
复制
    </ul>
代码语言:txt
复制
  </div>

转换成抽象树 (本质上就是一个js对象)

代码语言:txt
复制
{
代码语言:txt
复制
      tag: 'div',
代码语言:txt
复制
      attrs: [{name: 'class', value: 'box'}],
代码语言:txt
复制
      type: 1,
代码语言:txt
复制
      children: [
代码语言:txt
复制
        {
代码语言:txt
复制
          tag: 'h3',
代码语言:txt
复制
          attrs: [{name: 'class', value: 'title'}],
代码语言:txt
复制
          type: 1,
代码语言:txt
复制
          children: [
代码语言:txt
复制
            {text: '我是一个标题', type: 3}
代码语言:txt
复制
          ],
代码语言:txt
复制
        },
代码语言:txt
复制
        {
代码语言:txt
复制
          tag: 'ul',
代码语言:txt
复制
          attrs: [],
代码语言:txt
复制
          type: 1,
代码语言:txt
复制
          children: [
代码语言:txt
复制
            {
代码语言:txt
复制
              tag: 'li',
代码语言:txt
复制
              type: 1,
代码语言:txt
复制
              for: 'arr',
代码语言:txt
复制
              key: 'index',
代码语言:txt
复制
              alias: 'item',
代码语言:txt
复制
              children: [
代码语言:txt
复制
                      ...
代码语言:txt
复制
              ]
代码语言:txt
复制
            }
代码语言:txt
复制
          ]
代码语言:txt
复制
        }
代码语言:txt
复制
      ]
代码语言:txt
复制
    }

抽象语法树和虚拟节点区别

image.png

储备算法

  1. 找出以下字符串中,连续重复次数最多的字符undefined'aaaaaaaaaabbbbaaaaacccddbbbeeessfffffffffffffffffffffffffwwwwwwwwww'

这里我们用指针思想解决

image.png

代码语言:txt
复制
      var str =
代码语言:txt
复制
        'aaaaaaaaaabbbbaaaaacccddbbbeeessfffffffffffffffffffffffffwwwwwwwwww';
代码语言:txt
复制
      // 指针
代码语言:txt
复制
      var i = 0;
代码语言:txt
复制
      var j = 1;
代码语言:txt
复制
      // 当前重复最多的次数
代码语言:txt
复制
      var maxRepeat = 0;
代码语言:txt
复制
      // 重复最多的字符串
代码语言:txt
复制
      var maxStr = '';
代码语言:txt
复制
      // 循环 当i还在范围内
代码语言:txt
复制
      while (i <= str.length - 1) {
代码语言:txt
复制
        // 两个指针指向的字符 不相同
代码语言:txt
复制
        if (str[i] !== str[j]) {
代码语言:txt
复制
          // 与前面保存的最大重复次数比较, 更大的话重新赋值
代码语言:txt
复制
          if (j - i > maxRepeat) {
代码语言:txt
复制
            maxRepeat = j - i;
代码语言:txt
复制
            maxStr = str[i];
代码语言:txt
复制
          }
代码语言:txt
复制
          // i移动到j的位置
代码语言:txt
复制
          i = j;
代码语言:txt
复制
        }
代码语言:txt
复制
        // 如果相同的话 i不同,所以不用写
代码语言:txt
复制
        // j每次都要后移
代码语言:txt
复制
        j++;
代码语言:txt
复制
      }
代码语言:txt
复制
      console.log(`重复最多的字符是:${maxStr},重复次数为:${maxRepeat}`);
代码语言:txt
复制
      // 重复最多的字符是:f,重复次数为:25
  1. 递归算法undefined输出斐波那契数列前10项,1,1,2,3,5,8,13,21,34,55.

这个基本大家都会,就直接上代码了

代码语言:txt
复制
function fib(n) {
代码语言:txt
复制
        return n == 0 || n == 1 ? 1 : fib(n - 1) + fib(n - 2);
代码语言:txt
复制
}
代码语言:txt
复制
fib(6) // 13

然后假如我们是要求前10项之和,我们就可以优化一下。比如算fib(10)的时候,前面我们算过fib(9)和fib(8),直接取就行了。不用再算一遍了。

代码语言:txt
复制
      var cache = {};
代码语言:txt
复制
      function fib(n) {
代码语言:txt
复制
        if (cache.hasOwnProperty(n)) {
代码语言:txt
复制
          return cache[n];
代码语言:txt
复制
        }
代码语言:txt
复制
        var v = n == 0 || n == 1 ? 1 : fib(n - 1) + fib(n - 2);
代码语言:txt
复制
        cache[n] = v;
代码语言:txt
复制
        return v;
代码语言:txt
复制
      }
代码语言:txt
复制
      var num = 0;
代码语言:txt
复制
      for (let i = 0; i < 9; i++) {
代码语言:txt
复制
        num += fib(i);
代码语言:txt
复制
      }
代码语言:txt
复制
      console.log('num', num); // 88
  1. 递归 多维数组转嵌套对象undefined数组:[1, 2, [3, 4, 5, 6], 7, 8, 9]undefined对象:
代码语言:txt
复制
{
代码语言:txt
复制
  children: [
代码语言:txt
复制
    { value: 1 },
代码语言:txt
复制
    { value: 2 },
代码语言:txt
复制
    {
代码语言:txt
复制
      children: [
代码语言:txt
复制
        { value: 3 },
代码语言:txt
复制
        { children: [{ value: 4 }, { value: 5 }] },
代码语言:txt
复制
        { value: 6 },
代码语言:txt
复制
      ],
代码语言:txt
复制
    },
代码语言:txt
复制
    { value: 7 },
代码语言:txt
复制
    { children: [{ value: 8 }] },
代码语言:txt
复制
    { value: 9 },
代码语言:txt
复制
  ],
代码语言:txt
复制
};

小技巧: 出现了”规则复现“ 就想到用递归

代码语言:txt
复制
var arr = [1, 2, [3, [4, 5], 6], 7, [8], 9];
代码语言:txt
复制
//第一种 转换函数
代码语言:txt
复制
function convert(arr) {
代码语言:txt
复制
  // 结果数组
代码语言:txt
复制
  var result = [];
代码语言:txt
复制
  // 遍历
代码语言:txt
复制
  for (let i = 0; i < arr.length; i++) {
代码语言:txt
复制
    var item = arr[i];
代码语言:txt
复制
    if (typeof item === 'number') {
代码语言:txt
复制
      result.push({
代码语言:txt
复制
        value: item,
代码语言:txt
复制
      });
代码语言:txt
复制
    } else if (Array.isArray(item)) {
代码语言:txt
复制
      result.push({
代码语言:txt
复制
        children: convert(item),
代码语言:txt
复制
      });
代码语言:txt
复制
    }
代码语言:txt
复制
  }
代码语言:txt
复制
  return result;
代码语言:txt
复制
}
代码语言:txt
复制
// 第二种  转换函数
代码语言:txt
复制
function convert(item) {
代码语言:txt
复制
  if (typeof item == 'number') {
代码语言:txt
复制
    return { value: item };
代码语言:txt
复制
  } else if (Array.isArray(item)) {
代码语言:txt
复制
    return {
代码语言:txt
复制
      children: item.map((it) => convert(it)),
代码语言:txt
复制
    };
代码语言:txt
复制
  }
代码语言:txt
复制
}
代码语言:txt
复制
console.log('convert', convert(arr));

image.png

4、栈(后进先出 特点) 对于字符串不建议用递归,用指针比较好

smartRepeat函数,实现:

  • 3abc变为 abcabcabc
  • 3[2a2b] 变为 aabbaabbaabb
  • 2[1a3b2[3c4d]] 变为 abbbcccddddcccddddabbbcccddddcccddddundefined数字和字母不能混用,字母必须在[]中,且[]左边数字最小为1. 数字和字母可以多位,比如 12[abc]

思路:

image.png

代码语言:txt
复制
function smartRepeat(templateStr) {
代码语言:txt
复制
  //指针
代码语言:txt
复制
  var index = 0;
代码语言:txt
复制
  // 栈1 存数字  栈2 存字符型
代码语言:txt
复制
  var stack1 = [];
代码语言:txt
复制
  var stack2 = [];
代码语言:txt
复制
  // 剩余部分
代码语言:txt
复制
  var rest = templateStr;
代码语言:txt
复制
  // 最后一项单独处理 所有不用 <=
代码语言:txt
复制
  while (index < templateStr.length - 1) {
代码语言:txt
复制
    rest = templateStr.slice(index);
代码语言:txt
复制
    //判断剩余部分 是不是以 数字 和 [ 开头
代码语言:txt
复制
    if (/^\d+\[/.test(rest)) {
代码语言:txt
复制
      // 得到数字 (现在是字符串)
代码语言:txt
复制
      var times = rest.match(/^(\d+)\[/)[1];
代码语言:txt
复制
      // 指针移动 数字的长度 还要加上 左括号的 长度
代码语言:txt
复制
      index += times.length + 1;
代码语言:txt
复制
      // 栈1 和 栈2 压栈
代码语言:txt
复制
      stack1.push(+times);
代码语言:txt
复制
      stack2.push('');
代码语言:txt
复制
    }
代码语言:txt
复制
    // 剩余部分是 字母和 ] 开始
代码语言:txt
复制
    else if (/^[a-zA-Z]+\]/.test(rest)) {
代码语言:txt
复制
      // 得到字母
代码语言:txt
复制
      var words = rest.match(/^([a-zA-Z]+)\]/)[1];
代码语言:txt
复制
      // 栈2 栈顶  修改 为当前字母
代码语言:txt
复制
      stack2[stack2.length - 1] = words;
代码语言:txt
复制
      // 由于] 要单独处理,所有这里指针只移动 字母的长度即可
代码语言:txt
复制
      index += words.length;
代码语言:txt
复制
    }
代码语言:txt
复制
    // 剩余 部分是 ] 开始
代码语言:txt
复制
    else if (/^\]/.test(rest)) {
代码语言:txt
复制
      // 栈1 ,栈2 弹栈 并且 处理后 拼接到 栈2 的 新栈顶
代码语言:txt
复制
      var times = stack1.pop();
代码语言:txt
复制
      var words = stack2.pop();
代码语言:txt
复制
      stack2[stack2.length - 1] += words.repeat(times);
代码语言:txt
复制
      index++;
代码语言:txt
复制
    }
代码语言:txt
复制
  }
代码语言:txt
复制
  // while 循环结束
代码语言:txt
复制
  return stack2[0].repeat(stack1[0]);
代码语言:txt
复制
}
代码语言:txt
复制
console.log(smartRepeat('12[2[aab]2[c]]'));
代码语言:txt
复制
// aabaabccaabaabccaabaabccaabaabccaabaabccaabaabccaabaabccaabaabccaabaabccaabaabccaabaabccaabaabcc
手写 AST 抽象树

思路就和上面4的解法差不多。

代码语言:txt
复制
var templateString = `<div>
代码语言:txt
复制
  <h1>我是标题</h1>
代码语言:txt
复制
  <ul>
代码语言:txt
复制
    <li>A</li>
代码语言:txt
复制
    <li>B</li>
代码语言:txt
复制
    <li>C</li>
代码语言:txt
复制
    <li>D</li>
代码语言:txt
复制
  </ul>
代码语言:txt
复制
  <div>
代码语言:txt
复制
    <div>哈哈</div>
代码语言:txt
复制
  </div>
代码语言:txt
复制
</div>`
  • 遇到 <div> 把它放入栈1, 栈2 放入一个空数组
  • 遇到<h1> 也把它放入栈1,栈2 放入一个空数组。
  • 遇到"我是标题",栈2 的最后一个空数组中放入。

栈1: <div> <h1>undefined 栈2:

  • 遇到</h1>,栈1弹栈,栈2弹栈。并且把弹栈的数据整合到栈2的栈顶undefined.........

最后我们要变成的形式就是开始说的那种,不过我们先做简单的

代码语言:txt
复制
  {
代码语言:txt
复制
      tag: 'div',
代码语言:txt
复制
      attrs: [{name: 'class', value: 'box'}],
代码语言:txt
复制
      type: 1,
代码语言:txt
复制
      children: [
代码语言:txt
复制
        {
代码语言:txt
复制
          tag: 'h3',
代码语言:txt
复制
          attrs: [{name: 'class', value: 'title'}],
代码语言:txt
复制
          type: 1,
代码语言:txt
复制
          children: [
代码语言:txt
复制
            {text: '我是一个标题', type: 3}
代码语言:txt
复制
          ],
代码语言:txt
复制
        },       
代码语言:txt
复制
      ]
代码语言:txt
复制
    }

我们先来处理没有class等属性的情况

index.js

代码语言:txt
复制
import parse from './parse'
代码语言:txt
复制
var templateString = `<div>
代码语言:txt
复制
  <h1>我是标题</h1>
代码语言:txt
复制
  <ul>
代码语言:txt
复制
    <li>A</li>
代码语言:txt
复制
    <li>B</li>
代码语言:txt
复制
    <li>C</li>
代码语言:txt
复制
    <li>D</li>
代码语言:txt
复制
  </ul>
代码语言:txt
复制
  <div>
代码语言:txt
复制
    <div>哈哈</div>
代码语言:txt
复制
  </div>
代码语言:txt
复制
</div>`
代码语言:txt
复制
console.log(parse(templateString));

parse.js

代码语言:txt
复制
// parse 主函数
代码语言:txt
复制
export default function (templateString) {
代码语言:txt
复制
  // 指针
代码语言:txt
复制
  var index = 0
代码语言:txt
复制
  // 剩余字符串
代码语言:txt
复制
  var rest = templateString
代码语言:txt
复制
  // 开始标记 
代码语言:txt
复制
  var startRegExp = /^\<([a-z]+[1-6]?)\>/
代码语言:txt
复制
  // 结束标记
代码语言:txt
复制
  var endRegExp = /^\<\/([a-z]+[1-6]?)\>/
代码语言:txt
复制
  // 文字内容标记
代码语言:txt
复制
  var wordRegExp = /^([^\<]+)\<\/[a-z]+[1-6]?\>/
代码语言:txt
复制
  // 栈1 存标签名
代码语言:txt
复制
  var stack1 = []
代码语言:txt
复制
  // 栈2 存内容
代码语言:txt
复制
  var stack2 = [{children: []}]
代码语言:txt
复制
  while (index < templateString.length - 1) {
代码语言:txt
复制
    rest = templateString.slice(index)
代码语言:txt
复制
    // 识别开始  字符是不是 开始标签
代码语言:txt
复制
    if (startRegExp.test(rest)) {
代码语言:txt
复制
      // 获取 标签名
代码语言:txt
复制
      var tag = rest.match(startRegExp)[1]
代码语言:txt
复制
      // 将开始标记 推入 栈1
代码语言:txt
复制
      stack1.push(tag)
代码语言:txt
复制
      // 将空数组 推入 栈2
代码语言:txt
复制
      stack2.push({'tag': tag, children: []})
代码语言:txt
复制
      // 指针后移 长度加 上标签名长度 和 < > 的长度
代码语言:txt
复制
      index += tag.length + 2
代码语言:txt
复制
    }
代码语言:txt
复制
    // 识别结束
代码语言:txt
复制
    else if (endRegExp.test(rest)) {
代码语言:txt
复制
      var tag = rest.match(endRegExp)[1]
代码语言:txt
复制
      // 此时 tag 一定和 栈1的栈顶 是一样的
代码语言:txt
复制
      var pop_tag = stack1.pop()
代码语言:txt
复制
      if (tag === pop_tag) {
代码语言:txt
复制
        var pop_arr = stack2.pop()
代码语言:txt
复制
        if (stack2.length > 0) {
代码语言:txt
复制
          // 如果栈顶 没有children 属性,就创建一个数组children
代码语言:txt
复制
          if (!stack2[stack2.length - 1].hasOwnProperty('children')) {
代码语言:txt
复制
            stack2[stack2.length - 1].children = []
代码语言:txt
复制
          }
代码语言:txt
复制
          stack2[stack2.length - 1].children.push(pop_arr)
代码语言:txt
复制
        }
代码语言:txt
复制
      } else {
代码语言:txt
复制
        throw new Error(pop_tag + '标签没有封闭')
代码语言:txt
复制
      }
代码语言:txt
复制
      // 指针后移 长度加 上标签名长度 和 </ > 的长度
代码语言:txt
复制
      index += tag.length + 3
代码语言:txt
复制
    }
代码语言:txt
复制
    // 识别是内容
代码语言:txt
复制
    else if (wordRegExp.test(rest)) {
代码语言:txt
复制
      let word = rest.match(wordRegExp)[1]
代码语言:txt
复制
      // 不是全部 空字符的情况下
代码语言:txt
复制
      if (!/\s+/.test(word)) {
代码语言:txt
复制
        // 此时 改变 stack2 栈顶元素 children 属性
代码语言:txt
复制
        stack2[stack2.length - 1].children.push({ 'text': word, type: 3 })
代码语言:txt
复制
      }
代码语言:txt
复制
      index += word.length
代码语言:txt
复制
    }
代码语言:txt
复制
    // 其他的算文字  <img />这些先不考虑,主要学习思想
代码语言:txt
复制
    else {
代码语言:txt
复制
      index++
代码语言:txt
复制
    }
代码语言:txt
复制
  }
代码语言:txt
复制
  // 这里为啥没有剩余, 并且stack2初始要有一个children数组
代码语言:txt
复制
  // 是因为 </div>   识别结束 碰到这里就直接 index +=6 。直接就结束循环了
代码语言:txt
复制
  // 而上面 4 栈那里 是 ] 右中括号 就是最后一个。而index到不了最后一个,所以有剩余
代码语言:txt
复制
  // 增加一个默认children 数组就是为了 存最后的数据 
代码语言:txt
复制
  return stack2[0].children[0]
代码语言:txt
复制
}

ok,现在我们来处理 class等属性的情况

首先我们处理开始正则

代码语言:txt
复制
  // 开始标记  new add (\s[\<]+)? 获取标签里面的属性 <div class="name">
代码语言:txt
复制
  var startRegExp = /^\<([a-z]+[1-6]?)(\s[^\<]+)?\>/

然后再开始标签里面处理, 添加一个attrs对象

代码语言:txt
复制
    // 识别开始  字符是不是 开始标签
代码语言:txt
复制
    if (startRegExp.test(rest)) {
代码语言:txt
复制
      // 获取 标签名
代码语言:txt
复制
      var tag = rest.match(startRegExp)[1]
代码语言:txt
复制
      // 获取 属性 
代码语言:txt
复制
      var attrsString = rest.match(startRegExp)[2]
代码语言:txt
复制
      // 将开始标记 推入 栈1
代码语言:txt
复制
      stack1.push(tag)
代码语言:txt
复制
      // 将空数组 推入 栈2
代码语言:txt
复制
      stack2.push({'tag': tag, children: [], attrs: parseAttrsString(attrsString)})
代码语言:txt
复制
      // 指针后移 长度加 上标签名长度 和 < > 的长度 还要加上  属性的长度
代码语言:txt
复制
      var attrsStringLength = attrsString? attrsString.length: 0
代码语言:txt
复制
      index += tag.length + 2 + attrsStringLength
代码语言:txt
复制
    }

parseAttrsString函数是用来专门处理属性转换为数组的,

比如class="aa bb cc" id="myid" ====> [{name: 'class', value: 'aa bb cc'}, {name:

'id', value: 'myid'}]。 代码下面有,就不专门在贴一下次了

结束了。以下是完整代码

index.js

代码语言:txt
复制
import parse from './parse'
代码语言:txt
复制
var templateString = `<div>
代码语言:txt
复制
  <h1 class="aa bb cc" id="myId">我是标题</h1>
代码语言:txt
复制
  <ul>
代码语言:txt
复制
    <li>A</li>
代码语言:txt
复制
    <li>B</li>
代码语言:txt
复制
    <li>C</li>
代码语言:txt
复制
    <li>D</li>
代码语言:txt
复制
  </ul>
代码语言:txt
复制
  <div>
代码语言:txt
复制
    <div>哈哈</div>
代码语言:txt
复制
  </div>
代码语言:txt
复制
</div>`
代码语言:txt
复制
console.log(parse(templateString));

parse.js

代码语言:txt
复制
import parseAttrsString from './parseAttrsString'
代码语言:txt
复制
// parse 主函数
代码语言:txt
复制
export default function (templateString) {
代码语言:txt
复制
  // 指针
代码语言:txt
复制
  var index = 0
代码语言:txt
复制
  // 剩余字符串
代码语言:txt
复制
  var rest = templateString
代码语言:txt
复制
  // 开始标记  new add (\s[\<]+)? 获取标签里面的属性 <div class="name">
代码语言:txt
复制
  var startRegExp = /^\<([a-z]+[1-6]?)(\s[^\<]+)?\>/
代码语言:txt
复制
  // 结束标记
代码语言:txt
复制
  var endRegExp = /^\<\/([a-z]+[1-6]?)\>/
代码语言:txt
复制
  // 文字内容标记
代码语言:txt
复制
  var wordRegExp = /^([^\<]+)\<\/[a-z]+[1-6]?\>/
代码语言:txt
复制
  // 栈1 存标签名
代码语言:txt
复制
  var stack1 = []
代码语言:txt
复制
  // 栈2 存内容
代码语言:txt
复制
  var stack2 = [{children: []}]
代码语言:txt
复制
  while (index < templateString.length - 1) {
代码语言:txt
复制
    rest = templateString.slice(index)
代码语言:txt
复制
    // 识别开始  字符是不是 开始标签
代码语言:txt
复制
    if (startRegExp.test(rest)) {
代码语言:txt
复制
      // 获取 标签名
代码语言:txt
复制
      var tag = rest.match(startRegExp)[1]
代码语言:txt
复制
      // 获取 属性 
代码语言:txt
复制
      var attrsString = rest.match(startRegExp)[2]
代码语言:txt
复制
      // 将开始标记 推入 栈1
代码语言:txt
复制
      stack1.push(tag)
代码语言:txt
复制
      // 将空数组 推入 栈2
代码语言:txt
复制
      stack2.push({'tag': tag, children: [], attrs: parseAttrsString(attrsString)})
代码语言:txt
复制
      // 指针后移 长度加 上标签名长度 和 < > 的长度 还要加上  属性的长度
代码语言:txt
复制
      var attrsStringLength = attrsString? attrsString.length: 0
代码语言:txt
复制
      index += tag.length + 2 + attrsStringLength
代码语言:txt
复制
    }
代码语言:txt
复制
    // 识别结束
代码语言:txt
复制
    else if (endRegExp.test(rest)) {
代码语言:txt
复制
      var tag = rest.match(endRegExp)[1]
代码语言:txt
复制
      // 此时 tag 一定和 栈1的栈顶 是一样的
代码语言:txt
复制
      var pop_tag = stack1.pop()
代码语言:txt
复制
      if (tag === pop_tag) {
代码语言:txt
复制
        var pop_arr = stack2.pop()
代码语言:txt
复制
        if (stack2.length > 0) {
代码语言:txt
复制
          // 如果栈顶 没有children 属性,就创建一个数组children
代码语言:txt
复制
          if (!stack2[stack2.length - 1].hasOwnProperty('children')) {
代码语言:txt
复制
            stack2[stack2.length - 1].children = []
代码语言:txt
复制
          }
代码语言:txt
复制
          stack2[stack2.length - 1].children.push(pop_arr)
代码语言:txt
复制
        }
代码语言:txt
复制
      } else {
代码语言:txt
复制
        throw new Error(pop_tag + '标签没有封闭')
代码语言:txt
复制
      }
代码语言:txt
复制
      // 指针后移 长度加 上标签名长度 和 </ > 的长度
代码语言:txt
复制
      index += tag.length + 3
代码语言:txt
复制
    }
代码语言:txt
复制
    // 识别是内容
代码语言:txt
复制
    else if (wordRegExp.test(rest)) {
代码语言:txt
复制
      let word = rest.match(wordRegExp)[1]
代码语言:txt
复制
      // 不是全部 空字符的情况下
代码语言:txt
复制
      if (!/\s+/.test(word)) {
代码语言:txt
复制
        // 此时 改变 stack2 栈顶元素 children 属性
代码语言:txt
复制
        stack2[stack2.length - 1].children.push({ 'text': word, type: 3 })
代码语言:txt
复制
      }
代码语言:txt
复制
      index += word.length
代码语言:txt
复制
    }
代码语言:txt
复制
    // 其他的算文字  <img />这些先不考虑,主要学习思想
代码语言:txt
复制
    else {
代码语言:txt
复制
      index++
代码语言:txt
复制
    }
代码语言:txt
复制
  }
代码语言:txt
复制
  // 这里为啥没有剩余, 并且stack2初始要有一个children数组
代码语言:txt
复制
  // 是因为 </div>   识别结束 碰到这里就直接 index +=6 。直接就结束循环了
代码语言:txt
复制
  // 而上面 4 栈那里 是 ] 右中括号 就是最后一个。而index到不了最后一个,所以有剩余
代码语言:txt
复制
  // 增加一个默认children 数组就是为了 存最后的数据 
代码语言:txt
复制
  return stack2[0].children[0]
代码语言:txt
复制
}

parseAttrsString.js

代码语言:txt
复制
// 把属性转换为数组
代码语言:txt
复制
// class="aa bb cc" id="myid" ====> [{name: 'class', value: 'aa bb cc'}, {name: 'id', value: 'myid'}]
代码语言:txt
复制
export default function (attrsString) {
代码语言:txt
复制
  // 如果没有直接返回空数组
代码语言:txt
复制
  if (attrsString == undefined) return []
代码语言:txt
复制
  // 当前是否再引号里面
代码语言:txt
复制
  var isYinhao = false
代码语言:txt
复制
  // 断点
代码语言:txt
复制
  var point = 0
代码语言:txt
复制
  // 结果
代码语言:txt
复制
  var result = []
代码语言:txt
复制
  // 遍历
代码语言:txt
复制
  for (let i = 0; i < attrsString.length; i++) {
代码语言:txt
复制
    var char = attrsString[i]
代码语言:txt
复制
    // 碰到引号 把标记 取反
代码语言:txt
复制
    if (char === '"') {
代码语言:txt
复制
      isYinhao = !isYinhao
代码语言:txt
复制
    }
代码语言:txt
复制
    // 遇见 空格 并且 不 在引号中 
代码语言:txt
复制
    else if (char == ' ' && !isYinhao) {
代码语言:txt
复制
      // 获取属性 class="aa bb cc"
代码语言:txt
复制
      var item = attrsString.slice(point, i);
代码语言:txt
复制
      // 不是全部空格的情况先 结果数组 推入
代码语言:txt
复制
      if(!/^\s*$/.test(item)){
代码语言:txt
复制
        result.push(item.trim())
代码语言:txt
复制
        point = i
代码语言:txt
复制
      }
代码语言:txt
复制
    }
代码语言:txt
复制
  }
代码语言:txt
复制
  // 循环结束之后还剩一个 原因是 <div class="aa bb cc" id="myId">   myId与>没有空格,所以没有进入判断语句中
代码语言:txt
复制
  result.push(attrsString.slice(point).trim())
代码语言:txt
复制
  // 映射成对象
代码语言:txt
复制
  result = result.map(item=>{
代码语言:txt
复制
    // 拆分=
代码语言:txt
复制
    const o = item.match(/^(.+)="(.+)"$/)
代码语言:txt
复制
    return {
代码语言:txt
复制
      name: o[1],
代码语言:txt
复制
      value: o[2]
代码语言:txt
复制
    }
代码语言:txt
复制
  })
代码语言:txt
复制
  console.log('rrrrrrr', result);
代码语言:txt
复制
  return result
代码语言:txt
复制
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
作者已关闭评论
0 条评论
热度
最新
推荐阅读
目录
  • 抽象语法树和虚拟节点区别
  • 储备算法
    • 手写 AST 抽象树
    • 结束了。以下是完整代码
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档