这是我参与「第五届青训营 」笔记创作活动的第10天
本堂课重点内容
前端中数据结构,算法的重要性
实战案例分享
重点知识点介绍
1. 遍历所有文件
遍历所有文件的场景是很基础且常见的,比如生成页面侧边栏、生成文件树等场景。这里使用Node.js实现递归的DFS算法
// 使用Node.js实现递归的DFS算法
const fs = require('fs');
const path = require('path');
function scanDiretory(path){
let allFiles = [];
function readDiretory(path){
let files = fs.readdirSync(path);
files.forEach((file)=>{
let curPath = path + '/' + file;
let stat = fs.statSync(curPath);
if(stat.isDirectory()){
scanDiretory(curPath);
}else{
allFiles.push(curPath);
}
});
}
}
readDiretory(path);
return allFiles;
2. AST解析器
抽象语法树AST可以将代码解析为一颗对象树的结构,每个语法都对应对象树中的节点。借助对象树我们可以方便地进行语法分析和转换,其最主要的两个步骤是tokenize词法分析和parse语法分析
这用到了编译原理中的知识,编译器的前端部分主要是scanner和parser,我按照自己的理解编写了如下JavaScript代码
// 1. 首先,JavaScript引擎会将这段代码分成两个阶段:分词/词法分析和解析/语法分析。
// 2. 分词/词法分析阶段,JavaScript引擎会将代码分解成有意义的代码块,这些代码块被称为词法单元。
function scanner(input) {
let current = 0;
let tokens = [];
while (current < input.length) {
let char = input[current];
if (char === "(") {
tokens.push({
type: "paren",
value: "(",
});
current++;
continue;
}
if (char === ")") {
tokens.push({
type: "paren",
value: ")",
});
current++;
continue;
}
let WHITESPACE = /\s/;
if (WHITESPACE.test(char)) {
current++;
continue;
}
let NUMBERS = /[0-9]/;
if (NUMBERS.test(char)) {
let value = "";
while (NUMBERS.test(char)) {
value += char;
char = input[++current];
}
tokens.push({ type: "number", value });
continue;
}
if (char === '"') {
let value = "";
char = input[++current];
while (char !== '"') {
value += char;
char = input[++current];
}
char = input[++current];
tokens.push({ type: "string", value });
continue;
}
let LETTERS = /[a-z]/i;
if (LETTERS.test(char)) {
let value = "";
while (LETTERS.test(char)) {
value += char;
char = input[++current];
}
tokens.push({ type: "name", value });
continue;
}
throw new TypeError("I dont know what this character is: " + char);
}
return tokens;
}
// 3. 解析/语法分析阶段,JavaScript引擎会将词法单元流(数组)转换成一个由元素逐级嵌套所组成的代表了程序语法结构的树。这个树被称为“抽象语法树”(Abstract Syntax Tree,AST)。
function parser(tokens) {
let current = 0;
function walk() {
let token = tokens[current];
if (token.type === "number") {
current++;
return {
type: "NumberLiteral",
value: token.value,
};
}
if (token.type === "string") {
current++;
return {
type: "StringLiteral",
value: token.value,
};
}
if (token.type === "paren" && token.value === "(") {
token = tokens[++current];
let node = {
type: "CallExpression",
name: token.value,
params: [],
};
token = tokens[++current];
while (
token.type !== "paren" ||
(token.type === "paren" && token.value !== ")")
) {
node.params.push(walk());
token = tokens[current];
}
current++;
return node;
}
throw new TypeError(token.type);
}
let ast = {
type: "Program",
body: [],
};
while (current < tokens.length) {
ast.body.push(walk());
}
return ast;
}
3. 相似命令提示
在使用命令行时我们难免会输入错误的命令,此时命令行会提示我们最接近的命令(例如git),这个功能是基于最小距离算法实现的
// 命令行提示功能:使用最小距离算法实现最接近的命令
var fs = require("fs");
var leven = require("leven");
var wordListPath = require("word-list");
var readDictionary = function (path) {
path || (path = wordListPath);
return fs.readFileSync(path).toString().trim().split("\n");
};
var autocorrect = function (options) {
options || (options = {});
var dictionary = options.words || readDictionary(options.dictionary);
var len = dictionary.length;
return function (str) {
var distance, bestWord, i, word, min;
for (i = 0; i < len; i++) {
word = dictionary[i];
distance = leven(str, word);
if (distance === 0) {
return word;
} else if (min === undefined || distance < min) {
min = distance;
bestWord = word;
}
}
return bestWord;
};
};
module.exports = autocorrect;
课后个人总结
老师介绍了前端中一些要用到算法的场景,提到了DFS算法,最小距离算法等。前端不仅仅只有UI和交互开发,还有工程化的工具链、可视化、跨段等需要用到各自不同的算法。所以呢,前端学习还是有数据结构和算法的能力,持续地积累和思考这方面的知识才能更加有效地提高自己的水平。
引用参考
前端,与数据结构与算法专场(feishu.cn)