- name
- JavaScript分析器
- description
- 当进行JavaScript代码审查、性能优化、浏览器兼容性检查或安全审计时,分析JavaScript代码质量和最佳实践。
- license
- MIT
# JavaScript分析器技能
## 概述
JavaScript的灵活性是一把双刃剑。分析代码以防止运行时意外。
**核心原则**: JavaScript的灵活性是一把双刃剑。分析代码以防止运行时意外。
## 何时使用
**始终:**
- JavaScript代码审查
- 性能优化
- 浏览器兼容性检查
- 安全审计
- 代码重构
- 架构设计评审
**触发短语:**
- "分析JavaScript代码"
- "JavaScript性能优化"
- "代码质量检查"
- "JavaScript最佳实践"
- "浏览器兼容性"
- "安全漏洞检查"
## JavaScript分析功能
### 代码质量
- ESLint规则检查
- 代码风格统一
- 复杂度分析
- 重复代码检测
- 最佳实践验证
### 性能分析
- 执行时间测量
- 内存泄漏检测
- DOM操作优化
- 网络请求分析
- 渲染性能检查
### 安全检查
- XSS漏洞检测
- CSRF防护验证
- 输入验证检查
- 依赖安全扫描
- 敏感信息泄露
## 常见JavaScript问题
### 异步处理问题
```
问题:
异步操作处理不当
错误示例:
- 回调地狱
- Promise未正确处理
- async/await滥用
- 错误处理缺失
解决方案:
1. 使用async/await替代回调
2. 正确处理Promise错误
3. 避免阻塞主线程
4. 实现错误边界
```
### 内存泄漏
```
问题:
内存使用不当导致泄漏
错误示例:
- 事件监听器未移除
- 定时器未清理
- 闭包引用过多
- DOM节点引用未释放
解决方案:
1. 及时清理事件监听器
2. 清理定时器和间隔器
3. 避免不必要的闭包
4. 使用WeakMap/WeakSet
```
### 类型安全问题
```
问题:
JavaScript动态类型导致错误
错误示例:
- 类型转换错误
- 未定义属性访问
- 参数类型不匹配
- 返回值类型不确定
解决方案:
1. 使用TypeScript
2. 添加JSDoc类型注解
3. 运行时类型检查
4. 使用PropTypes
```
## 代码实现示例
### JavaScript代码分析器
```javascript
const fs = require('fs');
const path = require('path');
const { parse } = require('@babel/parser');
const traverse = require('@babel/traverse').default;
class JavaScriptAnalyzer {
constructor(options = {}) {
this.options = {
ecmaVersion: 2020,
sourceType: 'module',
plugins: [
'jsx',
'typescript',
'decorators-legacy',
'classProperties',
'objectRestSpread',
'asyncGenerators',
'functionBind',
'exportDefaultFrom',
'exportNamespaceFrom',
'dynamicImport',
'nullishCoalescingOperator',
'optionalChaining'
],
...options
};
this.issues = [];
this.metrics = {
totalFiles: 0,
totalLines: 0,
totalFunctions: 0,
totalClasses: 0,
complexityScore: 0,
maintainabilityIndex: 0
};
}
analyzeFile(filePath) {
try {
const content = fs.readFileSync(filePath, 'utf8');
const ast = this.parseCode(content, filePath);
if (!ast) {
return {
file: filePath,
error: 'Failed to parse',
issues: []
};
}
// 重置问题列表
this.issues = [];
// 执行各种分析
this.analyzeCodeQuality(ast, filePath);
this.analyzePerformance(ast, filePath);
this.analyzeSecurity(ast, filePath);
this.analyzeComplexity(ast, filePath);
this.analyzeBestPractices(ast, filePath);
return {
file: filePath,
issues: this.issues,
metrics: this.calculateFileMetrics(ast, content)
};
} catch (error) {
return {
file: filePath,
error: error.message,
issues: [{
type: 'parse_error',
severity: 'error',
message: `解析错误: ${error.message}`,
line: error.loc?.line || 0,
column: error.loc?.column || 0
}]
};
}
}
analyzeDirectory(directory) {
const results = [];
const allIssues = [];
function scanDirectory(dir) {
const items = fs.readdirSync(dir);
for (const item of items) {
const fullPath = path.join(dir, item);
const stat = fs.statSync(fullPath);
if (stat.isDirectory()) {
// 跳过node_modules等目录
if (!item.startsWith('.') && item !== 'node_modules') {
scanDirectory(fullPath);
}
} else if (isJavaScriptFile(item)) {
const result = this.analyzeFile(fullPath);
results.push(result);
allIssues.push(...(result.issues || []));
}
}
}
function isJavaScriptFile(fileName) {
const ext = path.extname(fileName);
return ['.js', '.jsx', '.ts', '.tsx', '.mjs'].includes(ext);
}
scanDirectory.call(this, directory);
return {
directory,
files: results,
summary: this.generateSummary(allIssues),
recommendations: this.generateRecommendations(allIssues)
};
}
parseCode(content, filePath) {
try {
return parse(content, {
...this.options,
filename: filePath
});
} catch (error) {
console.error(`解析文件失败: ${filePath}`, error);
return null;
}
}
analyzeCodeQuality(ast, filePath) {
// 检查变量命名
this.checkVariableNaming(ast, filePath);
// 检查函数长度
this.checkFunctionLength(ast, filePath);
// 检查代码重复
this.checkCodeDuplication(ast, filePath);
// 检查未使用变量
this.checkUnusedVariables(ast, filePath);
}
checkVariableNaming(ast, filePath) {
const namingPatterns = {
camelCase: /^[a-z][a-zA-Z0-9]*$/,
PascalCase: /^[A-Z][a-zA-Z0-9]*$/,
UPPER_CASE: /^[A-Z][A-Z0-9_]*$/,
snake_case: /^[a-z][a-z0-9_]*$/
};
traverse(ast, {
VariableDeclarator(path) {
const id = path.node.id;
if (id.type === 'Identifier') {
const name = id.name;
// 检查常量命名
if (path.parent.kind === 'const') {
if (!namingPatterns.UPPER_CASE.test(name) && name !== name.toLowerCase()) {
this.issues.push({
type: 'naming',
severity: 'warning',
message: `常量"${name}"应使用UPPER_CASE命名`,
line: id.loc?.line || 0,
column: id.loc?.column || 0,
suggestion: `改为${name.toUpperCase()}`
});
}
}
// 检查变量命名
else if (path.parent.kind === 'let' || path.parent.kind === 'var') {
if (!namingPatterns.camelCase.test(name)) {
this.issues.push({
type: 'naming',
severity: 'info',
message: `变量"${name}"应使用camelCase命名`,
line: id.loc?.line || 0,
column: id.loc?.column || 0,
suggestion: `改为${this.toCamelCase(name)}`
});
}
}
}
}
});
}
checkFunctionLength(ast, filePath) {
traverse(ast, {
FunctionDeclaration(path) {
this.checkFunctionSize(path, filePath);
},
FunctionExpression(path) {
this.checkFunctionSize(path, filePath);
},
ArrowFunctionExpression(path) {
this.checkFunctionSize(path, filePath);
}
});
}
checkFunctionSize(path, filePath) {
const functionNode = path.node;
const startLine = functionNode.loc?.start.line || 0;
const endLine = functionNode.loc?.end.line || 0;
const lines = endLine - startLine + 1;
if (lines > 50) {
const functionName = functionNode.id?.name || '匿名函数';
this.issues.push({
type: 'complexity',
severity: 'warning',
message: `函数"${functionName}"过长(${lines}行),建议拆分`,
line: startLine,
column: 0,
suggestion: '将函数拆分为更小的函数'
});
}
}
checkCodeDuplication(ast, filePath) {
// 简化实现:检查重复的函数调用
const functionCalls = new Map();
traverse(ast, {
CallExpression(path) {
const callee = path.node.callee;
if (callee.type === 'Identifier') {
const name = callee.name;
const line = callee.loc?.line || 0;
if (!functionCalls.has(name)) {
functionCalls.set(name, []);
}
functionCalls.get(name).push(line);
}
}
});
// 检查是否有重复调用
for (const [name, lines] of functionCalls) {
if (lines.length > 5) {
this.issues.push({
type: 'duplication',
severity: 'info',
message: `函数"${name}"被调用${lines.length}次,考虑提取为常量或缓存`,
line: Math.min(...lines),
column: 0,
suggestion: '考虑缓存函数结果或提取为常量'
});
}
}
}
checkUnusedVariables(ast, filePath) {
const declaredVariables = new Map();
const usedVariables = new Set();
// 收集声明的变量
traverse(ast, {
VariableDeclarator(path) {
const id = path.node.id;
if (id.type === 'Identifier') {
declaredVariables.set(id.name, {
line: id.loc?.line || 0,
column: id.loc?.column || 0
});
}
}
});
// 收集使用的变量
traverse(ast, {
Identifier(path) {
if (path.isReferencedIdentifier()) {
usedVariables.add(path.node.name);
}
}
});
// 检查未使用的变量
for (const [name, location] of declaredVariables) {
if (!usedVariables.has(name) && !name.startsWith('_')) {
this.issues.push({
type: 'unused_variable',
severity: 'warning',
message: `变量"${name}"已声明但未使用`,
line: location.line,
column: location.column,
suggestion: '删除未使用的变量或添加前缀_'
});
}
}
}
analyzePerformance(ast, filePath) {
this.checkDOMOperations(ast, filePath);
this.checkAsyncOperations(ast, filePath);
this.checkMemoryLeaks(ast, filePath);
}
checkDOMOperations(ast, filePath) {
traverse(ast, {
CallExpression(path) {
const callee = path.node.callee;
// 检查频繁的DOM查询
if (callee.type === 'MemberExpression') {
const object = callee.object;
const property = callee.property;
if (object.type === 'Identifier' && object.name === 'document') {
if (property.type === 'Identifier' &&
['querySelector', 'querySelectorAll', 'getElementById'].includes(property.name)) {
this.issues.push({
type: 'performance',
severity: 'info',
message: '频繁的DOM查询可能影响性能',
line: property.loc?.line || 0,
column: property.loc?.column || 0,
suggestion: '缓存DOM查询结果'
});
}
}
}
}
});
}
checkAsyncOperations(ast, filePath) {
traverse(ast, {
CallExpression(path) {
const callee = path.node.callee;
// 检查setTimeout/setInterval
if (callee.type === 'Identifier' &&
['setTimeout', 'setInterval'].includes(callee.name)) {
this.issues.push({
type: 'performance',
severity: 'warning',
message: `使用${callee.name}时确保清理定时器`,
line: callee.loc?.line || 0,
column: callee.loc?.column || 0,
suggestion: '在组件卸载时清理定时器'
});
}
}
});
}
checkMemoryLeaks(ast, filePath) {
traverse(ast, {
CallExpression(path) {
const callee = path.node.callee;
// 检查事件监听器
if (callee.type === 'MemberExpression') {
const property = callee.property;
if (property.type === 'Identifier' && property.name === 'addEventListener') {
this.issues.push({
type: 'memory_leak',
Ver no GitHub