对给定 JavaScript 代码的静态分析,找出其中未被使用的函数和变量,并输出到控制台。
相关文章
检测无用代码
功能清单
- 代码解析: 使用 Babel 的解析器 @babel/parser 将输入的 JavaScript 代码字符串解析为抽象语法树(AST),以便后续分析。
- 函数和变量定义收集: 在 AST 中遍历,收集代码中定义的函数和变量名称,并将它们分别存储在集合中。
- 函数调用和变量引用收集: 继续遍历 AST,收集代码中使用的函数和变量名称,并将它们分别存储在集合中。
- 未被使用的函数和变量查找: 通过比较定义的函数和变量集合与使用的函数和变量集合,找出未被使用的函数和变量。
- 结果输出: 将找出的未被使用的函数和变量输出到控制台,以供开发者查看和分析。
一、解析代码为 AST(抽象语法树)
使用适当的工具(如 Babel、@babel/parser、@vue/parser 等)将代码解析为 AST。这样可以方便地分析代码的结构和关系。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| const { parse } = require('@babel/parser');
const code = `export function a(x) { return x * x; } function b(x) { return x * x * x; } function c(x) { return x * x * x; } export const d = 3.14; let e = 5; var f = 5; b(e); `;
const ast = parse(code, { sourceType: 'module', });
|
二、收集函数和变量的定义
遍历 AST,收集函数和变量的定义。这可以通过查找函数声明、变量声明等节点来完成。
1 2 3 4 5 6 7 8 9 10 11 12 13
| const definedFunctions = new Set(); const definedVariables = new Set();
traverse(ast, { FunctionDeclaration(path) { definedFunctions.add(path.node.id.name); }, VariableDeclarator(path) { definedVariables.add(path.node.id.name); }, });
|
三、收集函数调用和变量引用
继续遍历 AST,收集函数调用和变量引用。这可以通过查找函数调用、变量引用等节点来完成。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const usedFunctions = new Set(); const usedVariables = new Set();
traverse(ast, { CallExpression(path) { if (path.node.callee.type === 'Identifier') { usedFunctions.add(path.node.callee.name); } }, Identifier(path) { const parent = path.findParent(parentPath => { return parentPath.isVariableDeclarator() || parentPath.isFunctionDeclaration(); }); if (!parent) { usedVariables.add(path.node.name); } }, });
|
四、分析使用情况
根据收集到的函数和变量的定义,以及它们的调用和引用情况,分析代码中哪些函数和变量是被使用的,哪些是未被使用的。
1 2 3 4 5 6
| const unusedFunctions = Array.from(definedFunctions).filter(name => !usedFunctions.has(name)); const unusedVariables = Array.from(definedVariables).filter(name => !usedVariables.has(name));
console.log('Unused functions:', unusedFunctions); console.log('Unused variables:', unusedVariables);
|
五、完整示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| const { parse } = require('@babel/parser'); const traverse = require('@babel/traverse').default;
const code = `export function a(x) { return x * x; } function b(x) { return x * x * x; } function c(x) { return x * x * x; } export const d = 3.14; let e = 5; var f = 5; b(e); `;
const ast = parse(code, { sourceType: 'module', plugins: ['jsx'], });
const definedFunctions = new Set(); const definedVariables = new Set();
traverse(ast, { FunctionDeclaration(path) { definedFunctions.add(path.node.id.name); }, VariableDeclarator(path) { definedVariables.add(path.node.id.name); }, });
const usedFunctions = new Set(); const usedVariables = new Set();
traverse(ast, { CallExpression(path) { if (path.node.callee.type === 'Identifier') { usedFunctions.add(path.node.callee.name); } }, Identifier(path) { const parent = path.findParent(parentPath => { return parentPath.isVariableDeclarator() || parentPath.isFunctionDeclaration(); }); if (!parent) { usedVariables.add(path.node.name); } }, });
const unusedFunctions = Array.from(definedFunctions).filter(name => !usedFunctions.has(name)); const unusedVariables = Array.from(definedVariables).filter(name => !usedVariables.has(name));
console.log('Unused functions:', unusedFunctions); console.log('Unused variables:', unusedVariables);
|
输出结果
1 2
| Unused functions: [ 'a', 'c' ] Unused variables: [ 'd', 'f' ]
|