抽象语法树是什么


抽象语法树被之上就是一个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 = '';
while (i <= str.length - 1) {
if (str[i] !== str[j]) {
if (j - i > maxRepeatCount) {
maxRepeatCount = j - i;
maxRepeatChar = str[i];
}
i = 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>
var cache = {};
function fib(n) {
if (cache.hasOwnProperty(n)) {
return cache[n];
}
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>
function smartRepeat(templateStr) {
var index = 0;
var stack1 = [];
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('');
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++;
}
console.log(index, stack1, stack2);
}
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';
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);
if (startRegExp.test(rest)) {
let tag = rest.match(startRegExp)[1];
let attrsString = rest.match(startRegExp)[2];
stack1.push(tag);
stack2.push({ 'tag': tag, 'children': [], 'attrs': parseAttrsString(attrsString) });
const attrsStringLength = attrsString != null ? attrsString.length : 0;
index += tag.length + 2 + attrsStringLength;
} else if (endRegExp.test(rest)) {
let tag = rest.match(endRegExp)[1];
let pop_tag = stack1.pop();
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 + '标签没有封闭!!');
}
index += tag.length + 3;
} else if (wordRegExp.test(rest)) {
let word = rest.match(wordRegExp)[1];
if (!/^\s+$/.test(word)) {
stack2[stack2.length - 1].children.push({ 'text': word, 'type': 3 });
}
index += word.length;
} else {
index++;
}
}
return stack2[0].children[0];
};
parseAttrsString.js
export default function (attrsString) {
if (attrsString == undefined) return [];
console.log(attrsString);
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;
}
}
}
result.push(attrsString.substring(point).trim());
result = result.map(item => {
const o = item.match(/^(.+)="(.+)"$/);
return {
name: o[1],
value: o[2]
};
});
return result;
}
|