Babel 处理没有参数的自执行函数
需求
处理没有参数的自执行函数,将其代码块放到函数外,简化代码
注意
- 为什么处理的自执行函数不能含参?防止该参数名在其他地方有使用(全局变量、其它函数)
- 自执行涉及定义变量时,不能做处理,代码放到函数外可能会出错(运行、逻辑)
下面是代码样例(encode.js)
var d = 'd3d';
!function (d) { var d = '[' + d + ']'; console.log('hello world -- ' + d); }(d)
console.log('d is ' + d);
!function (s) { var d = '<' + s + '>'; console.log('hello world -- ' + d); }(d)
console.log('d is ' + d);
!function () { d = '(' + d + ')'; console.log('hello world -- ' + d); }('test')
console.log('d is ' + d);
!function () { d = '~' + d + '~'; console.log('hello world -- ' + d); }()
console.log('d is ' + d);
处理后代码(decode.js)
var d = 'd3d';
!function (d) {
var d = '[' + d + ']';
console.log('hello world -- ' + d);
}(d);
console.log('d is ' + d);
!function (s) {
var d = '<' + s + '>';
console.log('hello world -- ' + d);
}(d);
console.log('d is ' + d);
d = '(' + d + ')';
console.log('hello world -- ' + d);
console.log('d is ' + d);
d = '~' + d + '~';
console.log('hello world -- ' + d);
console.log('d is ' + d);
思路
单纯把自执行函数内代码块拿到函数外很简单,重点是要记得特征判断
在在线解析网站 ast explore 对比有无参数的自执行函数,避免操作有参数的自执行函数
自执行函数的节点类型为 UnaryExpression
编写 babel 插件
完整插件代码如下
// decrypt.js
const fs = require('fs');
var util = require('util');
const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const types = require('@babel/types');
const generator = require('@babel/generator').default;
// 程序启动时间
var time_start = new Date().getTime()
// 读取文件
process.argv.length > 2 ? encode_file = process.argv[2] : encode_file = 'encode.js';
process.argv.length > 3 ? decode_file = process.argv[3] : decode_file = 'decode.js';
let jscode = fs.readFileSync(encode_file, { encoding: 'utf-8' });
console.log(util.format('Reading the file [%s] is complete.', encode_file))
// 转换为 ast 树
let ast = parser.parse(jscode);
const visitor =
{
UnaryExpression(path) {
let { argument, operator } = path.node;
if (!types.isCallExpression(argument) || operator !== '!') {
return;
}
let { callee } = argument;
if (!types.isFunctionExpression(callee)) {
return;
}
let { id, params, body } = callee;
// 注意,不能使用 path.node.argument.arguments 长度作为该自执行参数是否为无参类型判断
// 因为自执行函数可以被强行传参(如示例还原前代码)
if (id !== null || params.length > 0) {
return;
}
path.replaceWithMultiple(body.body);
}
}
//调用插件,处理待处理 js ast 树
traverse(ast, visitor);
console.log('AST traverse completed.')
// 生成处理后的 js
let { code } = generator(ast);
console.log('AST generator completed.')
fs.writeFile(decode_file, code, (err) => { });
console.log(util.format('The javascript code in [%s] has been processed.', encode_file))
console.log(util.format('The processing result has been saved to [%s].', decode_file))
// 程序结束时间
var time_end = new Date().getTime()
console.log(util.format('The program runs to completion, time-consuming: %s s', (time_end - time_start) / 1000))