AST(Abstract Syntax Tree, AST)抽象语法树,可以把代码转译成语法树的表现形式
例如下面的代码:
var a = 3;
a + 5
AST抽象出来的树结构:
Program代表的是根节点
VariableDeclaration变量声明
BinaryExpression(二项式)
可以到 astexplorer.net 查看AST的解析结果
大多数编译器的工作过程可以分为三部分:
安装 esprima 来理解编译的过程:
npm install esprima estraverse escodegen
const esprima = require('esprima')
const estraverse = require('estraverse')
const escodegen = require('escodegen')
let code = `var a = 3`
// Parse(解析)
let ast = esprima.parseScript(code);
//Transform(转换)
estraverse.traverse(ast, {
enter(node) {
console.log("enter",node.type);
},
leave(node) {
console.log("leave",node.type);
}
});
// Generate(代码生成)
const result = escodegen.generate(ast);
babel 对于 AST 的遍历是深度优先遍历,对于 AST 上的每一个分支 Babel 都会先向下遍历走到尽头,然后再向上遍历退出刚遍历过的节点,然后寻找下一个分支。
AST 对语法树的遍历是 深度优先遍历,所以会先向下遍历走到尽头,然后再向上遍历退出刚遍历过的节点,寻找下一个分支,所以遍历的过程中控制台会打印下面的信息:
enter Program
enter VariableDeclaration
enter VariableDeclarator
enter Identifier
leave Identifier
enter Literal
leave Literal
leave VariableDeclarator
leave VariableDeclaration
leave Program
通过type的判断我们可以修改变量的值:
estraverse.traverse(ast, {
enter(node) {
if(node.type === "Literal"){
node.value = "change";
}
}
});
// var a = "change";
来看下 babel是如何工作的, 首先通过npm安装 @babel/core和 babel-types:
npm install @babel/core
我们知道babel能编译es6代码,例如最基础的const和箭头函数:
// es2015 的 const 和 arrow function
const add = (a, b) => a + b;
// Babel 转译后
var add = function add(a, b) {
return a + b;
};
我们可以到 astexplorer 查看生成的语法树:
{
"type": "Program",
"body": [
{
"type": "VariableDeclaration", // 变量声明
"declarations": [ // 具体声明
{
"type": "VariableDeclarator", // 变量声明
"id": {
"type": "Identifier", // 标识符(最基础的)
"name": "add" // 函数名
},
"init": {
"type": "ArrowFunctionExpression", // 箭头函数
"id": null,
"expression": true,
"generator": false,
"params": [ // 参数
{
"type": "Identifier",
"name": "a"
},
{
"type": "Identifier",
"name": "b"
}
],
"body": { // 函数体
"type": "BinaryExpression", // 二项式
"left": { // 二项式左边
"type": "Identifier",
"name": "a"
},
"operator": "+", // 二项式运算符
"right": { // 二项式右边
"type": "Identifier",
"name": "b"
}
}
}
}
],
"kind": "const"
}
],
"sourceType": "module"
}
通过代码模拟一下:
const babel = require('babel-core');
const t = require('babel-types');
let code = `let add = (a, b)=>{return a+b}`;
let ArrowPlugins = {
visitor: {
ArrowFunctionExpression(path) {
let { node } = path;
let body = node.body;
let params = node.params;
let r = t.functionExpression(null, params, body, false, false);
path.replaceWith(r);
}
}
}
let result = babel.transform(code, {
plugins: [
ArrowPlugins
]
})
console.log(result.code);
我们可以在访问者visitor中捕获到匹配的type,在回调函数里面替换箭头函数。
const babel = require("@babel/core");
const typs = require("@babel/types");
const code = `
class Animal {
constructor(name){
this.name = name
}
getName(){
return this.name
}
}
`
const classPlugins = {
visitor:{
ClassDeclaration(path){
let node = path.node;
let body = node.body.body;
let id = node.id;
let params = node.params;
let methods = body.map(method=>{
if(method.kind === "constructor"){
return typs.functionDeclaration(id, method.params, method.body)
}else{
// Animal.prototype
let left = typs.memberExpression(id,typs.identifier("prototype"));
// Animal.prototype.getName
left = typs.memberExpression(left,method.key);
let right = typs.functionExpression(null,method.params,method.body);
return typs.assignmentExpression("=",left,right);
}
})
path.replaceWithMultiple(methods);
}
}
}
const result = babel.transform(code, {
plugins: [classPlugins]
})
console.log(result.code)
const babel = require('@babel/core');
const types = require('@babel/types');
const code = `import antd,{Button} from "antd"`;
const importPlugin = {
visitor: {
ImportDeclaration(path) {
let node = path.node
let specifiers = node.specifiers
if (
!(
specifiers.length == 1 &&
types.isImportDefaultSpecifier(specifiers[0])
)
) {
specifiers = specifiers.map((specifier) => {
let local = types.importDefaultSpecifier(specifier.local);
if (types.isImportDefaultSpecifier(specifier)) {
return types.importDeclaration([local],types.stringLiteral(node.source.value))
} else {
return types.importDeclaration([local],types.stringLiteral(node.source.value+"/lib/"+specifier.local.name))
}
});
path.replaceWithMultiple(specifiers)
}
},
},
}
const result = babel.transform(code, {
plugins: [importPlugin],
});
console.log(result.code)
来自:https://segmentfault.com/a/1190000027079474
向web前端开发者整理提供的chrome插件或应用:比如Postman、JSON Viewer、Page Ruler 、ChromeADB 等等
作为前端开发,我们都习惯使用一些开源的插件例如jquery工具库,那么如何使用原生js来开发封装一个自己的插件呢?接下来就看一下怎么去开发一个自己的js插件,先上代码
jquery.typeahead.js是一款高级的自动补全jQuery插件。该自动补全插件提供超过50个配置选项和回调方法,用于完成自动补全功能,能够完成绝大部分表单自动补全的需求。
这篇文章为大家分享图片轮播插件,最全最简单最通用的 幻灯片轮播插件,pc端和移动端都可完美使用,能满足绝大部分网站的轮播需求。js轮播插件包括Swiper、slick、owl carousel2、jssor/slider 、iSlider 等
在上个项目中,客户希望时间选择插件可以是ios风格的那种,但是找了很久,发现并没有用vue的ios风格时间插件,于是自己便自己造了一个轮子.插件依赖于better-scroll和vue
在前端开发中,使用Visual Studio Code有哪些你常用的插件?推荐几个自己喜欢的,不带链接,自己搜索安装吧。这些都是比较实用、前端必备的插件集
常用谷谷歌浏览器确实没有其它国产软件的内置功能丰富。但是 Google 浏览器的的优点恰恰就体现在拥有超简约的界面,以及支持众多强大好用的扩展程序,用户能够按照自己的喜好去个性化定制浏览器。今天我就给大家介绍几款自己常用的插件。
安装Sublime text 2插件很方便,可以直接下载安装包解压缩到Packages目录,也可以安装package control组件,然后直接在线安装
BlockUI 插件是用于进行AJAX操作时模拟同步传输时锁定浏览器操作。当它被激活时,它会阻止使用者与页面(或页面的一部分)进行交互,直至它被取消。BlockUI以在DOM中添加元素的方法来实现阻止用户与浏览器交互的外观和行为
使用vscode开发vue项目的时候,从远端拉下一个新的项目后,安装完依赖后跑起项目时,发现直接报了一堆语法错误:包括换行、空格、单双引号、分号等各种格式问题
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!