Skip to content

pilipala233/variableStatistics

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

JavaScript 死代码检测

基于 AST 的静态分析工具,用于检测 JavaScript 代码中的死代码(未使用的变量和函数)。

🎯 功能特性

  • 准确的作用域分析:正确处理函数作用域、变量提升、嵌套函数
  • 三级分类
    • 🟢 使用中:被引用的变量/函数
    • 🟡 可疑:顶层声明但未引用(可能被其他文件使用)
    • 🔴 死代码:嵌套声明且未引用(可安全删除)
  • 未声明引用检测:识别未声明就使用的变量/函数(避免运行时错误)
  • 导出识别:自动识别 window.xxx = yyycap.xxx = yyy 等导出模式
  • 完整的引用追踪:记录每个变量的所有引用位置
  • 可视化界面:清晰展示分析结果,包含代码位置和作用域路径

📦 快速开始

1. 安装依赖

npm install

2. 生成 AST

node index.js <你的js文件路径>

示例:

# 分析示例文件
node index.js examples/test.js

# 分析你自己的文件
node index.js path/to/your-file.js

这会在相同目录生成对应的 .json 文件(AST)。

3. 修改 analyzer.html 加载路径

打开 analyzer.html,找到第 1101 行左右:

const response = await fetch('./examples/test.json');  // 修改为你的 JSON 文件路径

4. 运行分析

在浏览器中打开 analyzer.html,查看分析结果。

🏗️ 核心实现原理

三阶段分析法

Stage 1: 构建符号表和作用域链
   ↓
Stage 2: 收集引用关系
   ↓
Stage 3: 死代码检测

阶段 1:构建符号表和作用域链

  • 遍历 AST,识别所有声明(var/let/const/function
  • 为每个函数创建独立的作用域
  • 构建作用域树(父子关系链)
  • 处理 JavaScript 特性:
    • var 变量提升和重复声明合并
    • 函数表达式的匿名作用域
    • 参数声明
  • 关联 AST 节点和作用域(用于引用阶段匹配)

关键数据结构

// 符号表
symbolTable = Map {
    "0_x_10" => {
        name: "x",
        type: "variable",
        scope: 0,
        scopePath: "global",
        references: []
    }
}

// 作用域树
scopeTree = {
    id: 0,
    type: "global",
    declarations: Map { "x" => "0_x_10" },
    children: [/* 子作用域 */],
    parent: null
}

阶段 2:收集引用关系

  • 再次遍历 AST,识别所有标识符引用
  • 使用作用域链解析引用(findSymbolInScope
  • 向上查找作用域,模拟 JavaScript 的标识符解析
  • 记录每个引用的位置和所在作用域
  • 检测未声明的引用

作用域解析示例

var x = 1;
function test() {
    var x = 2;
    console.log(x);  // 找到局部的 x (2),不是全局的 x (1)
}

阶段 3:死代码检测

  • 标记规则:
    • references.length > 0使用中
    • scopeLevel === 0 && references.length === 0可疑(顶层未引用)
    • scopeLevel > 0 && references.length === 0死代码(嵌套未引用)
  • 保守策略:保护所有顶层声明,避免误删被其他文件使用的代码

为什么作用域链是核心?

作用域链构建 = 从语法到语义的关键一跃

AST(语法结构)          作用域链(语义结构)
    |                           |
    | 遍历 + 语义规则             |
    ↓                           ↓
节点嵌套关系  -------→  标识符解析规则

如果作用域链错误

  • ❌ 同名变量无法区分
  • ❌ 变量提升无法处理
  • ❌ 引用解析全部错误
  • ❌ 所有后续分析都是错的

📁 项目结构

variableStatistics/
├── analyzer.html          # 🎯 主分析器(在浏览器中打开)
├── index.js               # AST 生成工具
├── package.json           # 项目依赖
├── .gitignore            # Git 忽略配置
├── README.md             # 项目文档
└── examples/             # 测试用例目录
    ├── README.md         # 测试用例说明
    ├── test.js           # 基础语法测试
    ├── test.json         # test.js 的 AST(自动生成)
    ├── test2.js          # 99%的真实项目文件测试(加了一点用于测试的代码)
    ├── test2.json        # test2.js 的 AST(自动生成)
    ├── test3.js          # 基础语法测试
    └── test3.json        # test3js 的 AST(自动生成)

核心文件

文件 说明
analyzer.html 🎯 主分析器,在浏览器中打开
index.js AST 生成工具(使用 acorn 解析 JS 文件)
examples/ 测试用例目录,包含 test.js 和 test2.js

测试用例

详见 examples/README.md

⚠️ 局限性与无法处理的场景

1. 动态特性

// ❌ 动态属性访问
var name = "getUserName";
obj[name]();  // 无法分析 name 是否被使用

// ❌ eval / Function 构造器
eval("var x = 1");
new Function("return x")();  // 无法静态分析

// ❌ with 语句
with(obj) {
    prop = 1;  // 无法确定 prop 是哪个变量
}

原因:静态分析无法预测运行时行为。


2. 跨文件引用

// file1.js
function apiHandler() { ... }  // 被其他文件调用

// file2.js
<script src="file1.js"></script>
apiHandler();  // 无法追踪跨文件引用

当前处理:顶层声明标记为"可疑",提示手动检查。

解决方案:需要实现模块依赖图分析(类似 Webpack)。


3. 对象方法的 this 引用

var obj = {
    name: "test",
    getName: function() {
        return this.name;  // this.name 无法追踪到 obj.name
    }
};

原因this 绑定是运行时确定的,静态分析无法处理。


4. 间接引用和反射

// ❌ 间接调用
var funcName = "myFunc";
window[funcName]();

// ❌ 反射 API
Object.keys(obj).forEach(key => {
    obj[key]();  // 无法分析哪些方法被调用
});

// ❌ 解构赋值中的复杂模式
var { [computedKey]: value } = obj;

5. 闭包和高阶函数

function createCounter() {
    var count = 0;  // ✅ 可以检测
    return function() {
        return count++;  // ✅ 可以检测到引用
    };
}

// 但以下场景有限制:
function wrapper(callback) {
    var internal = 1;
    callback(internal);  // ✅ 可以检测
}
wrapper(function(x) {
    externalVar = x;  // ⚠️ 如果 externalVar 未声明,会报未声明引用
});

当前处理:可以检测闭包内的引用,但无法追踪回调函数传递的复杂场景。


6. ES6+ 新特性

// ✅ 完整支持 let/const 块级作用域(v2.1+)
if (true) {
    let x = 1;  // 正确:x 在 if 块作用域
}
console.log(x);  // 正确:报未声明引用

// ✅ for 循环块作用域
for (let i = 0; i < 10; i++) {
    // i 在 for 块作用域
}
console.log(i);  // 正确:报未声明引用

// ❌ 解构赋值中的嵌套
var { a: { b: c } } = obj;  // 可能无法正确处理深层解构

// ❌ import/export
import { foo } from './module';  // 未处理 ES6 模块

// ❌ Class 语法
class MyClass {
    #privateField;  // 未处理私有字段
}

7. 框架特定模式

// ❌ Vue 模板引用
<template>
    <div>{{ message }}</div>  // message 在 JS 中声明,但被模板使用
</template>

// ❌ React JSX
function Component() {
    const handleClick = () => {};
    return <button onClick={handleClick} />;  // JSX 需要特殊处理
}

// ❌ jQuery 插件模式
$.fn.myPlugin = function() { ... };

当前处理:这些需要框架特定的分析器。


8. 全局对象和宿主环境

// ⚠️ 浏览器全局对象
document.getElementById('app');  // document 被标记为未声明引用
console.log("test");  // console 被标记为未声明引用

// ⚠️ Node.js 全局对象
require('./module');  // require 被标记为未声明引用
process.env.NODE_ENV;  // process 被标记为未声明引用

当前处理:会报告为"未声明引用"。

改进方案:维护全局对象白名单(window, document, console, require, etc.)。


9. Getter/Setter 副作用

var obj = {
    get value() {
        sideEffect();  // 访问 obj.value 时会调用
        return this._value;
    }
};

var x = obj.value;  // 看起来只是读取,实际执行了 sideEffect()

原因:静态分析无法检测属性访问的副作用。


10. 性能限制

// 超大文件(10000+ 行)
// 深度嵌套(20+ 层函数)
// 大量声明(1000+ 变量)

当前处理:纯前端分析,大文件可能导致浏览器卡顿。

改进方案

  • 移到 Node.js 环境(Worker 多线程)
  • 增量分析
  • 缓存中间结果

📊 最佳实践建议

✅ 适合使用的场景

  1. 单文件分析:独立的工具函数、业务逻辑文件
  2. 传统 JS 代码:ES5/ES6 基础语法,无复杂框架依赖
  3. 代码清理前的参考:识别明显的死代码
  4. 代码 Review:发现未使用的辅助函数

⚠️ 使用时注意

  1. 顶层"可疑"代码需手动确认:可能被其他文件引用
  2. 未声明引用需检查:可能是全局对象或拼写错误
  3. 结果仅供参考:删除前务必测试
  4. 配合其他工具:ESLint、TypeScript 等

无关紧要

这个项目是我毕业一年时,在公司为甲方维护旧代码、但必须依赖他们的低代码内网平台时抽空研究出来的。test2.js 里的源码其实就是当年项目的真实逻辑(具体内容只能从提交历史里看)。他们的平台前端只会把顶层声明的函数罗列出来,完全看不出哪些已经废弃、哪些仍在使用,于是我才萌生了做这个项目的想法:目标很简单——辨认那些明明声明了却肯定不会被用到的函数,好让我心里踏实地删掉它们。

只可惜后来项目忙、能力有限,再加上换了工作,这个计划就半途而废。四年里我一直耿耿于怀。直到最近有空,又赶上 AI 编程的大发展,我才重新拾起提交。AI 的思路和我当初差不多,但写得又快又准;也许我准备的测试样本还不够多,肉眼看没问题,却可能还有遗漏,测试覆盖也可能不够全面。但对于四年前我定下的目标,它已经帮我圆满实现了。我相信AI编程是不可逆的趋势。

📝 许可证

MIT License


About

基于 AST 的静态分析工具,用于检测 JavaScript 代码中的死代码

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published