Vue源码:抽象语法树
抽象语法树是什么
抽象语法树被之上就是一个JS对象
抽象语法树和虚拟节点的关系
内容
相关算法储备 — 指针思想
试寻找字符串中,连续重复次数最多的字符。
指针就是下标,不是C语言中的指针,C语言中的指针可以操作内存。JS 中的指针就是一个下标位置。
i: 0
j: 1
- 如果i和j指向的字一样,那么i不动,j后移
- 如果i和j指向的字不一样,此时说明它们之间的字都是连续相同的,让i追上j,j后移
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><script>// 试寻找字符串中,连续重复次数最多的字符。var str = 'abbbccc';// 指针var i = 0;var j = 1;// 当前重复次数最多的次数var maxRepeatCount = 0;// 重复次数最多的字符串var maxRepeatChar = '';// 当i还在范围内的时候,应该继续寻找while (i <= str.length - 1) {// 看i指向的字符和j指向的字符是不是不相同if (str[i] !== str[j]) {// console.log('报!!!' + i + '和' + j + '之间的文字连续相同!!都是字母' + str[i] + '它重复了' + (j - i) + '次');// 和当前重复次数最多的进行比较if (j - i > maxRepeatCount) {// 如果当前文字重复次数(j - i)超过了此时的最大值// 就让它成为最大值maxRepeatCount = j - i;// 将i指针指向的字符存为maxRepeatCharmaxRepeatChar = str[i];}// 让指针i追上指针ji = j;}// 不管相不相同,j永远要后移j++;}// 循环结束之后,就可以输出答案了console.log(maxRepeatChar + '重复了' + maxRepeatCount + '次,是最多的连续重复字符');</script>
</body></html>
相关算法储备 — 递归深入
试输出斐波那契数列的前10 项,即1、1、2、3、5、8、13 、21 、34 、55 。然后请思考,代码是否有大量重复的计算?应该如何解决重复计算的问题?
cache思想
{"0": 1,"1": 1,"2": 2,"3": 3,"4": 5
}
形式转换:试将高维数组[1, 2, [3, [4, 5], 6], 7, [8], 9] 变为图中所示的对
象
小技巧:只要出现了“规则复现”就要想到用递归。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><script>// 试输出斐波那契数列的前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));}</script>
</body></html>
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><script>let array = [1, 2, [3, [4, 5], 6], 7, [8], 9];function help(obj, arr) {if (!obj.children) obj.children = [];arr.forEach(item => {if (Array.isArray(item)) obj.children.push(help({}, item));else obj.children.push({value: item});})return obj;}console.log(help({}, array));function convert(item) {if (typeof item === 'number') return {value: item}else if (Array.isArray(item)) return {children: item.map(_item => {return convert(_item)})}}console.log(convert(array))
</script>
</body>
</html>
相关算法储备 — 栈
- 栈(stack )又名堆栈,它是一种运算受限的线性表,仅在表尾能进行入和删除操作。这一端被称为栈顶,相对地,把另一端称为栈底。
- 向一个栈插入新元素又称作进栈、入栈或压栈;从一个栈删除元素又称作出栈或退栈。
- 后进先出(LIFO )特点:栈中的元素,最先进栈的必定是最后出栈,后进栈的一定会先出栈
- JavaScript 中,栈可以用数组模拟。需要限制只能使用push() 和pop() ,不能使用unshift() 和shift()。即,数组尾是栈顶。
- 当然,可以用面向对象等手段,将栈封装的更好。
利用"栈"的题目
试编写"智能重复"smartRepeat函数,实现:
- 将3[abc]变为abcabcabc
- 将3[2[a]2[b]]变为aabbaabbaabb
- 将2[1[a]3[b]2[3[c]4[d]]]变为abbbcccddddcccddddabbbcccddddcccdddd
不用考虑输入字符串是非法的情况,比如:
- 2[a3[b]]是错误的,应该补一个1,即2[1[a]3[b]]
- [abc]是错误的,应该补一个1,即1[abc]
使用"栈"优雅接替
词法分析的时候,经常要用到栈这个数据结构;
初学者大坑:栈的题目和递归非常像,这类题目给人的感觉都是用递归解题。信心满满动手开始写了,却发现递归怎么都递归不出来。此时就要想到,不是用递归,而是用栈。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><script>// 试编写“智能重复”smartRepeat函数,实现:// 将3[abc]变为abcabcabc// 将3[2[a]2[b]]变为aabbaabbaabb // 将2[1[a]3[b]2[3[c]4[d]]]变为abbbcccddddcccddddabbbcccddddcccddddfunction smartRepeat(templateStr) {// 指针var index = 0;// 栈1,存放数字var stack1 = [];// 栈2,存放临时字符串var stack2 = [];// 剩余部分var rest = templateStr;while (index < templateStr.length - 1) {// 剩余部分rest = templateStr.substring(index);// 看当前剩余部分是不是以数字和[开头if (/^\d+\[/.test(rest)) {// 得到这个数字let times = Number(rest.match(/^(\d+)\[/)[1]);// 就把数字压栈,把空字符串压栈stack1.push(times);stack2.push('');// 让指针后移,times这个数字是多少位就后移多少位加1位。// 为什么要加1呢?加的1位是[。index += times.toString().length + 1;} else if (/^\w+\]/.test(rest)) {// 如果这个字符是字母,那么此时就把栈顶这项改为这个字母let word = rest.match(/^(\w+)\]/)[1];stack2[stack2.length - 1] = word;// 让指针后移,word这个词语是多少位就后移多少位index += word.length;} else if (rest[0] === ']') {// 如果这个字符是],那么就①将stack1弹栈,②stack2弹栈,③把字符串栈的新栈顶的元素重复刚刚弹出的那个字符串指定次数拼接到新栈顶上。let times = stack1.pop();let word = stack2.pop();// repeat是ES6的方法,比如'a'.repeat(3)得到'aaa'stack2[stack2.length - 1] += word.repeat(times);index++;}console.log(index, stack1, stack2);}// while结束之后,stack1和stack2中肯定还剩余1项。返回栈2中剩下的这一项,重复栈1中剩下的这1项次数,组成的这个字符串。如果剩的个数不对,那就是用户的问题,方括号没有闭合。return stack2[0].repeat(stack1[0]);}var result = smartRepeat('3[2[3[a]1[b]]4[d]]');console.log(result);</script>
</body></html>
正则表达式的相关方法
手写实现AST抽象语法树
识别attrs
完整代码
index.js
import parse from './parse.js';var templateString = `<div><h3 class="aa bb cc" data-n="7" id="mybox">你好</h3><ul><li>A</li><li>B</li><li>C</li></ul>
</div>`;const ast = parse(templateString);
console.log(ast);
parse.js
import parseAttrsString from './parseAttrsString.js';// parse函数,主函数
export default function (templateString) {// 指针var index = 0;// 剩余部分var rest = '';// 开始标记var startRegExp = /^\<([a-z]+[1-6]?)(\s[^\<]+)?\>/;// 结束标记var endRegExp = /^\<\/([a-z]+[1-6]?)\>/;// 抓取结束标记前的文字var wordRegExp = /^([^\<]+)\<\/[a-z]+[1-6]?\>/;// 准备两个栈var stack1 = [];var stack2 = [{ 'children': [] }];while (index < templateString.length - 1) {rest = templateString.substring(index);// console.log(templateString[index]);if (startRegExp.test(rest)) {// 识别遍历到的这个字符,是不是一个开始标签let tag = rest.match(startRegExp)[1];let attrsString = rest.match(startRegExp)[2];// console.log('检测到开始标记', tag);// 将开始标记推入栈1中stack1.push(tag);// 将空数组推入栈2中stack2.push({ 'tag': tag, 'children': [], 'attrs': parseAttrsString(attrsString) });// 得到attrs字符串的长度const attrsStringLength = attrsString != null ? attrsString.length : 0;// 指针移动标签的长度加2再加attrString的长度,为什么要加2呢?因为<>也占两位index += tag.length + 2 + attrsStringLength;} else if (endRegExp.test(rest)) {// 识别遍历到的这个字符,是不是一个结束标签let tag = rest.match(endRegExp)[1];// console.log('检测到结束标记', tag);let pop_tag = stack1.pop();// 此时,tag一定是和栈1顶部的是相同的if (tag == pop_tag) {let pop_arr = stack2.pop();if (stack2.length > 0) {stack2[stack2.length - 1].children.push(pop_arr);}} else {throw new Error(pop_tag + '标签没有封闭!!');}// 指针移动标签的长度加3,为什么要加2呢?因为</>也占3位index += tag.length + 3;} else if (wordRegExp.test(rest)) {// 识别遍历到的这个字符,是不是文字,并别不能是全空let word = rest.match(wordRegExp)[1];// 看word是不是全是空if (!/^\s+$/.test(word)) {// 不是全是空 // console.log('检测到文字', word);// 改变此时stack2栈顶元素中stack2[stack2.length - 1].children.push({ 'text': word, 'type': 3 });}// 指针移动标签的长度加3,为什么要加2呢?因为</>也占3位index += word.length;} else {index++;}}// 此时stack2就是我们之前默认放置的一项了,此时要返回这一项的children即可return stack2[0].children[0];
};
parseAttrsString.js
// 把attrsString变为数组返回
export default function (attrsString) {if (attrsString == undefined) return [];console.log(attrsString);// 当前是否在引号内var isYinhao = false// 断点var point = 0;// 结果数组var result = [];// 遍历attrsString,而不是你想的用split()这种暴力方法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());// 下面的代码功能是,将["k=v","k=v","k=v"]变为[{name:k, value:v}, {name:k, value:v}, {name:k,value:v}];result = result.map(item => {// 根据等号拆分const o = item.match(/^(.+)="(.+)"$/);return {name: o[1],value: o[2]};});return result;
}
Vue源码:抽象语法树相关推荐
- vue 初始化方法_前端发展方向指南—Vue源码初始化
Vue 的源码结构比较绕,同时使用了大量的面向对象的高级技巧.重写方法,扩展方法,多态等应用.从 Vue 实例的加载过程就可以看出来,这一节重点看看 Vue 的源码加载流程是什么. 前言 vue已是目 ...
- 详解AST抽象语法树
浅谈 AST 先来看一下把一个简单的函数转换成AST之后的样子. // 简单函数 function square(n) {return n * n; }// 转换后的AST {type: " ...
- 什么是php的ast结构,什么是AST?Vue源码中AST语法树的解析
这篇文章给大家介绍的内容是关于什么是AST?Vue源码中AST语法树的解析,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 什么是AST AST是指抽象语法树(abstract syn ...
- Vue源码流程图(函数名与源码对应)
这里写目录标题 概览 1. 变化侦查 1.1 Observer流程图 2. vdom虚拟DOM 2.1 创建节点createElm 2.2 更新节点patchVnode 2.3 更新子节点 updat ...
- Vue 源码解读(11)—— render helper
当学习成为了习惯,知识也就变成了常识. 感谢各位的 关注.点赞.收藏和评论. 新视频和文章会第一时间在微信公众号发送,欢迎关注:李永宁lyn 文章已收录到 github 仓库 liyongning/b ...
- html转换成抽象语法树,五分钟了解抽象语法树(AST)babel是如何转换的?
抽象语法树 什么是抽象语法树? It is a hierarchical program representation that presents source code structure acco ...
- Vue源码解析之Template转化为AST的实现方法
什么是AST 在Vue的mount过程中,template会被编译成AST语法树,AST是指抽象语法树(abstract syntax tree或者缩写为AST),或者语法树(syntax tree) ...
- Vue源码阅读(12):解析器
今天聊聊解析器,解析器的作用是将程序员编写的模板字符串解析成抽象语法树,抽象语法树可以理解成模板字符串的对象表示形式,其本质并没有什么神奇的,只不过是 JS 中最为常见的对象字面量. 通过抽象语法树, ...
- Vue源码之mustache模板引擎(一)
Vue源码之mustache模板引擎(一) 个人练习结果仓库(持续更新):Vue源码解析 抽空把之前学的东西写成笔记. 学习视频链接:[尚硅谷]Vue源码解析之mustache模板引擎 模板引擎是什么 ...
- PHP程序运行流程:语法分析(Parse)与抽象语法树(AST),PHP-Parser
什么是抽象语法树? 在 PHP5中,从 php 脚本到 Opcodes 的执行的过程是: Lexing:词法扫描分析,将源文件转换成 Token 流: Parsing:语法分析,在此阶段生成 Opco ...
最新文章
- Mysql系列七:分库分表技术难题之分布式全局唯一id解决方案
- 2.1.3 正则化(归一化)的用处以及何时使用
- 改变循环执行的状态,循环程序举例
- MYSQL5.7版本sql_mode=only_full_group_by问题
- E. 数好多好多猴(南阳理工oj—21新生第二场招新赛)
- Java多线程学习二十四:阻塞队列包含哪些常用的方法?add、offer、put 等方法的区别?
- 技术人员如何持续不断的成长?
- 【10天基于STM32F401RET6智能锁项目实战第2天】用按键点灯----GPIO的输入和输出
- 成为一名PHP专家其实并不难
- mysql导入sql文件出错的一种解决方法
- allwinner 全志uboot git网址 及其他相关链接
- 最全最新cpu显卡天梯图_显卡天梯图,CPU天梯图汇总(大概是最全的天梯图)第三期...
- 子网掩码计算器java源码
- 论文阅读笔记(audio-visual相关)—Co-Separating Sounds of Visual Objects
- Excel技巧:如何将数值改成以万为单位,且保留小数点两位?
- 兰州大学计算机专业硕士考研经验贴
- 1 一文详细阐述UI设计颜色搭配,受益匪浅~
- asp net core 微信公众号开发 (一)
- n1服务器系统和小钢炮,教你N1小钢炮系统设置中其他一些应用和服务器的设置的相关方法教程...
- 穆迪收购Omega Performance,加强在线信贷培训平台
热门文章
- 17.TCP/IP 详解卷1 --- TCP: 传输控制协议
- 10. SQL 注入
- 10. jQuery Callback 函数
- Oracle 触发器详解
- PADS 改变图纸和图页边界大小
- Linux安装配置redis 、启动redis、redis设置密码
- King's Quest - poj 1904(强连通分量+外挂输入输出)
- 64位Windows7环境下,Eclipse集成svn后出现Failed to load JavaHL Library的解决办法
- 深入理解Yii2.0 (2)事件Event
- WinForm窗体生命周期