核心思想
解析 JavaScript 主要涉及以下几个任务,难度递增:

- 代码格式化/美化:将压缩的 JS 代码还原成可读的格式。
- 静态分析:在不执行代码的情况下,提取信息,如变量名、函数名、函数参数、依赖的库等。
- 代码转换/重构:将 ES6+ 代码转译成 ES5 代码,或者修改 AST(抽象语法树)来实现代码混淆或优化。
- 执行 JavaScript:在 Python 环境中运行 JS 代码并获取结果。
针对这些任务,我们有不同的工具和库。
使用现成的命令行工具(推荐用于转译和美化)
这是最常用、最可靠的方法,特别是对于代码转译(如 Babel)和格式化(如 Prettier),Python 的 subprocess 模块可以方便地调用这些外部工具。
Babel (代码转译)
Babel 是一个 JavaScript 编译器,主要用于将新版本的 JavaScript (ES6+) 代码转换为向后兼容的 JavaScript 版本(如 ES5)。
步骤:

-
安装 Node.js 和 npm:Babel 是基于 Node.js 的,所以必须先安装它们。
-
全局安装 Babel 命令行工具:
npm install --global @babel/cli @babel/core @babel/preset-env
-
创建一个配置文件
.babelrc在你的项目根目录:{ "presets": ["@babel/preset-env"] } -
在 Python 中调用 Babel:
(图片来源网络,侵删)假设你有一个
input.js文件,内容是 ES6 代码:// input.js const add = (a, b) => { return a + b; }; console.log(add(1, 2));Python 代码可以这样调用 Babel 进行转译:
import subprocess import os input_js_path = "input.js" output_js_path = "output_es5.js" # 确保输入文件存在 if not os.path.exists(input_js_path): print(f"Error: Input file '{input_js_path}' not found.") exit() try: # 构建命令 # babel input.js --out-file output_es5.js command = [ "babel", input_js_path, "--out-file", output_js_path ] # 执行命令 result = subprocess.run(command, capture_output=True, text=True, check=True) print("Babel transpilation successful!") print(f"Output written to: {output_js_path}") # 打印转译后的代码 with open(output_js_path, 'r') as f: print("\nTranspiled Code:") print(f.read()) except FileNotFoundError: print("Error: 'babel' command not found. Please install Babel globally.") except subprocess.CalledProcessError as e: print(f"Babel transpilation failed with error:") print(e.stderr)转译后的
output_es5.js文件内容会是类似这样的 ES5 代码:"use strict"; function add(a, b) { return a + b; } console.log(add(1, 2));
Prettier (代码格式化)
Prettier 是一个“有主见的”代码格式化工具,可以统一代码风格。
步骤:
-
全局安装 Prettier:
npm install --global prettier
-
在 Python 中调用 Prettier:
Python 代码可以这样调用 Prettier 进行格式化:
import subprocess import os messy_js_path = "messy.js" pretty_js_path = "pretty.js" # 假设 messy.js 内容是一行压缩的代码 messy_js_content = "const add=(a,b)=>{return a+b;};console.log(add(1,2));" with open(messy_js_path, 'w') as f: f.write(messy_js_content) try: # 构建命令 # prettier messy.js --write pretty.js command = [ "prettier", messy_js_path, "--write", pretty_js_path ] # 执行命令 subprocess.run(command, check=True) print("Prettier formatting successful!") print(f"Formatted code written to: {pretty_js_path}") # 打印格式化后的代码 with open(pretty_js_path, 'r') as f: print("\nFormatted Code:") print(f.read()) except FileNotFoundError: print("Error: 'prettier' command not found. Please install Prettier globally.") except subprocess.CalledProcessError as e: print(f"Prettier formatting failed with error: {e}")
使用 Python 库进行静态分析 (AST)
如果你想直接在 Python 中分析 JS 代码的结构(提取所有函数定义),而不想依赖外部命令行工具,可以使用纯 Python 的 JS 解析库。
推荐库:esprima (Python 封装)
esprima 是一个广泛使用的 JavaScript 解析器,有 Python 封装,它能将 JS 代码转换成 抽象语法树,AST 是代码结构化的表示,你可以遍历它来获取任何你想要的信息。
安装:
pip install esprima
示例:提取所有函数名和参数
import esprima
import json
js_code = """
function greet(name) {
console.log("Hello, " + name);
}
const multiply = (x, y) => {
return x * y;
};
// 这是一个调用,不是定义
greet("World");
"""
try:
# 解析 JS 代码,生成 AST
# syntax 是一个字典,包含了 AST 的根节点
# program 是根节点,body 是一个包含所有顶级语句的列表
syntax = esprima.parseScript(js_code)
# 为了方便查看,可以打印 AST (json.dumps 可以美化输出)
# print(json.dumps(syntax, indent=2))
# 遍历 AST 来提取信息
# 我们只关心函数声明和函数表达式
for node in syntax['body']:
# 函数声明 (function myFunc() {})
if node['type'] == 'FunctionDeclaration':
func_name = node['id']['name']
params = [p['name'] for p in node['params']]
print(f"Found Function Declaration: '{func_name}' with params: {params}")
# 函数表达式 (const myFunc = function() {} 或 const myFunc = () => {})
elif node['type'] == 'VariableDeclaration':
for declaration in node['declarations']:
if declaration['init'] and declaration['init']['type'] in ['FunctionExpression', 'ArrowFunctionExpression']:
func_name = declaration['id']['name']
params = [p['name'] for p in declaration['init']['params']]
func_type = declaration['init']['type']
print(f"Found {func_type}: '{func_name}' with params: {params}")
except esprima.Error as e:
print(f"JS Parsing Error: {e}")
输出:
Found Function Declaration: 'greet' with params: ['name']
Found ArrowFunctionExpression: 'multiply' with params: ['x', 'y']
通过这种方式,你可以进行更复杂的静态分析,比如查找未使用的变量、分析代码依赖关系等。
执行 JavaScript 代码
如果你需要真正地运行 JS 代码并获取其返回值,可以使用 Python 的 JS 运行时环境。
推荐库:PyExecJS
PyExecJS 是一个多后端的 JS 执行器,它不自带 JS 引擎,而是调用你系统上已有的 JS 环境(如 Node.js、PyV8 等)。
安装:
pip install PyExecJS
示例:执行 JS 函数并获取结果
import execjs
# 1. 准备你的 JavaScript 代码
js_code = """
// 定义一个函数
function fibonacci(n) {
if (n <= 1) {
return n;
}
return fibonacci(n - 1) + fibonacci(n - 2);
}
// 导出一个函数,让 Python 可以调用
// PyExecJS 会寻找 module.exports 或全局变量
exports.calculate = function(n) {
return fibonacci(n);
};
"""
# 2. 创建一个运行时环境(它会自动寻找可用的 JS 环境,如 Node.js)
# 如果环境变量 `EXECJS_RUNTIME` 已设置,会使用指定的环境
ctx = execjs.compile(js_code)
# 3. 调用 JS 函数并传入参数
# ctx.call("全局函数名", 参数)
# ctx.call("模块名.函数名", 参数)
try:
result = ctx.call("calculate", 10)
print(f"The 10th Fibonacci number is: {result}") # 应该输出 55
# 另一个例子:执行一段代码并获取最后的结果
another_result = ctx.eval("2 + 2 * 3")
print(f"The result of '2 + 2 * 3' is: {another_result}") # 应该输出 8
except execjs.ProgramError as e:
print(f"JavaScript execution error: {e}")
except execjs.RuntimeUnavailableError as e:
print(f"No JS runtime available. Please install Node.js or another JS engine. Error: {e}")
重要提示:PyExecJS 的性能远不如原生代码,对于计算密集型任务,它会很慢,它最适合用于调用一些已有的、逻辑不复杂的 JS 库接口。
总结与选择
| 方法 | 工具/库 | 主要用途 | 优点 | 缺点 |
|---|---|---|---|---|
| 命令行工具 | Babel, Prettier |
代码转译、格式化 | 功能强大、稳定、生态成熟 | 依赖 Node.js 环境,需要额外安装 |
| 静态分析 | esprima (Python) |
提取代码结构、变量、函数等 | 纯 Python 实现,无需 JS 环境 | 功能局限于解析,无法执行代码 |
| 代码执行 | PyExecJS |
运行 JS 代码并获取结果 | 灵活,能利用现有 JS 库 | 性能较差,不适合计算密集型任务 |
如何选择?
- 如果你的目标是转换或美化 JS 代码:首选方法一(Babel/Prettier + subprocess),这是行业标准,最可靠。
- 如果你的目标是分析 JS 代码的结构(比如写一个静态分析工具):使用方法二(esprima),它能让你在 Python 中深度探索代码。
- 如果你的目标是运行一小段 JS 代码并获取结果(比如调用一个 JS 库的简单函数):使用方法三(PyExecJS),但要非常注意其性能瓶颈。
对于大多数开发场景,方法一和方法二的组合已经能够覆盖绝大部分需求。
