JavaScript压缩

用逐字符状态机删掉 JS 里的注释和多余空白,保留字符串、正则字面量和模板字符串原样,并给出压缩前后的字符数、字节数与压缩率。这不是 Terser、UglifyJS 那种 AST 级压缩:变量名不会被改写,也不做作用域分析。

选项

提示:输入框里按 Ctrl + Enter(macOS 用 Cmd + Enter)直接压缩;单独按 Enter 是换行。

#原始写法压缩后原字符压缩后字符压缩率
1var a = 1; // 行注释var a=1;17852.9%
2var a = 1; /* 块注释 */var a=1;20860.0%
3/*! 版权 2026 */\nvar a = 1;/*! 版权 2026 */\nvar a=1;25238.0%
4var u = "https://a.com";var u="https://a.com";24228.3%
5var re = /a\/b/g;var re=/a\/b/g;171511.8%
6var x = a / b / c;var x=a/b/c;181233.3%
7var t = `a\n b`;var t=`a\n b`;161412.5%
8function f() {\n return 1;\n}function f(){return 1;}282317.9%
9var a = b\n(function () { })()var a=b\n(function(){})()292417.2%
10if (a) {\n b();\n}if(a){b();}171135.3%
11var obj = { a: 1, b: 2 };var obj={a:1,b:2};251828.0%
12for (var i = 0; i < 3; i++) {\n x += i;\n}for(var i=0;i<3;i++){x+=i;}412734.1%

表里的 \n 表示一个换行符。以上 16 行是本工具对自己跑一遍的真实结果。

使用方法

  1. 把要处理的 JavaScript 粘进左边「输入 JavaScript」框。页面已经预置了 9 行演示代码(function total (list) 那一段,186 个字符),打开即可直接跑。
  2. 确认两个选项:默认勾选「保留 /*! */ 版权注释」和「必要时保留换行」。前者保护许可证声明,后者防止删掉换行后触发自动分号插入(ASI)改变语义。
  3. 点「压缩」,或在输入框里按 Ctrl + Enter(macOS 用 Cmd + Enter),右侧立刻给出结果。
  4. 看结果下方的统计条:压缩后字符数、压缩率、原始字符数、压缩前后 UTF-8 字节数、删掉与补回的字符数。演示代码从 186 字符压到 132 字符,压缩率 29.03%。
  5. 确认无误后点「复制结果」或「下载 .js」。下载的是 UTF-8 无 BOM 文件,可以直接被打包工具读取。

这个 JavaScript压缩 工具能解决什么问题

四类场景用得上:一是把临时写的脚本、书签小工具、邮件模板里嵌的内联脚本压成几行,减少粘贴时的体积和噪声;二是想快速知道「去掉注释和缩进之后这段代码到底有多重」,用来判断要不要上构建流程;三是复制别人的示例代码时先去掉大段教学注释,只留可执行部分;四是在没法用 Node、也没装构建工具的环境里(比如只有浏览器的公用电脑)做一次轻量瘦身。

它明确不做这些事:不把 calculateTotalPrice 改写成 a,不分析作用域,不做死代码消除,不合并常量,不转换语法。这些是 Terser、UglifyJS、esbuild 这类基于抽象语法树(AST)的压缩器干的活,需要完整的解析器和几百 KB 的代码体积;本页是纯前端即时工具,做不到也不打算做。如果你的目标是上线构建产物的体积,请直接使用构建工具链,本页只当快速估算和临时瘦身用。另外,CSS 和 HTML 各有各的压缩规则(比如 CSS 要处理 @media 内的空格、HTML 要保护 pre 里的空白),本页只实现 JavaScript 词法,用底部列出的专门工具更合适。

压缩原理与规则说明

本工具不解析成语法树,而是一个逐字符状态机,一次线性扫描得到结果,时间复杂度 O(n)。它维护 5 个状态:普通代码、单引号字符串、双引号字符串、模板字符串、以及跳过中的注释。字符串和模板字符串里的内容整段原样拷贝,包括其中的空格、换行和转义序列——所以 "a b c" 里的连续空格一个都不会少。

为什么不用一条正则全局替换?最直觉的写法是 src.replace(/\/\/[^\n]*/g, '') 删行注释,但它会把字符串里的双斜杠一起吃掉:var u = "https://a.com"; 会被截成 var u = "https:,直接变成语法错误。注释只存在于「代码」状态,不在字符串里,这个区别无法用一条正则表达,只能逐字符跟踪状态。

正则字面量 vs 除号是本算法唯一需要"猜"的地方,因为 / 在 ECMAScript(ECMA-262)里既是除号也是正则字面量的开头。判据沿用 JSMin 的思路,看前一个有效字符 last:如果 last 为空(代码开头),或者属于 ( ) , = : [ ! & | ? { } ; + - * % ~ ^ < > 这一组运算符与标点,或者前一个词是 return typeof instanceof in of new delete void do else yield await case 之一,那么 / 一定是正则开头,进入「扫描到未转义的 / 为止,且要跳过 [...] 字符类内部的 /」;否则当作除号原样输出。这就是为什么 var x = a / b / c; 能正确压成 var x=a/b/c;,而 var re = /a\/b/g; 里的 \/ 不会被误当成正则结尾。

删除换行是最危险的一步,因为 ECMAScript 的自动分号插入(ASI)只在源码里真的有行终止符时才生效。把换行删掉,ASI 就不再触发,语义可能整个变掉。本工具用三条保守规则,宁可少删一个字节也不改语义:

行内的多余空白照常删除,只在三种情况下补回一个空格:两侧都是词字符(var sum 不能变成 varsum)、两侧都是 + 或 -(a + +b 不能变成 a++b)、数字后面紧跟 .(1 .toString() 不能变成 1.toString())。以 /*! 或 /* @license 开头的块注释整段保留,这是开源许可证声明的通行约定。

完整示例(数值仅作演示)

演示代码共 9 行、186 个字符、210 个 UTF-8 字节(字节数比字符数多 24,是因为注释里的中文按 UTF-8 各占 3 字节)。压缩后为 2 行、132 个字符、132 字节:

这段代码的变量名 total、list、sum、re 在输出里一个都没变。这就是本工具与 AST 级压缩的分界线:只删「不影响词法的空白和注释」,不动「标识符」。

输入范围与对照表

工具区那张 16 行对照表是本工具对自己跑一遍的真实结果,默认显示 12 行,点「加载更多」展开全部,也可以导出 CSV(带 BOM)或复制成 TSV。下面这张表按「会被怎么处理」归类,方便对照规则:

写法会被怎么处理规则依据
// 行注释 / /* 块注释 */整段删除只存在于代码状态,17→8 与 20→8 字符
/*! 版权 2026 */整段保留许可证声明,压缩率仅 8.0%
var u = "https://a.com";字符串里的 // 不动处于字符串状态,24→22 字符
var re = /a\/b/g;正则里的 \/ 不动字符类与转义被跳过,17→15 字符
var x = a / b / c;按除号处理前一个字符是标识符,18→12 字符
var t = `a` 换行 `b`;模板内换行保留换行是字符串内容,有语义
var a = b 换行 (function(){})()换行保留下一行以 ( 开头,ASI 危险
return 换行 1;换行保留受限产生式,删了会改变返回值
var big = 9007199254740993;数字一个字符不动不改写字面量,27→25 字符
var n = 1.50;数字一个字符不动不改写数值表示,13→11 字符
label: for (;;) { break label; }换行删掉,标签保留上一行以 { 结尾,34→27 字符
var s = "a b c";字符串内空白不动字符串是数据,19→17 字符

输入体积阈值:小于 204800 字符(200 KB)即时处理;超过 200 KB 会在结果区提示体积后照常处理;超过 2097152 字符(2 MB)直接拒绝并说明原因——字符级扫描虽然是 O(n),但一次性处理几 MB 文本会让标签页明显卡顿,这是保护而不是限制。

输入范围、边界与精度说明

常见问题

为什么变量名没有被缩短?这不是压缩失败了吗?

不是失败,是本工具的设计边界。缩短标识符必须先把代码解析成抽象语法树,再分析每个变量的作用域,确认改名不会撞上外层同名变量或 with / eval 的动态作用域。这是 Terser(-m 参数)、UglifyJS、esbuild 做的事。本工具只做词法层面的空白与注释删除,不碰标识符,所以输出一定只是输入程序的"另一种写法"。

压缩率只有 29%,是不是太低了?

29.03% 是只删注释和空白的正常水平。真实项目里 AST 级压缩能到 60% 以上,主要来自变量名缩短、死代码消除和常量折叠这三块,而这三块本工具都不做。想知道这段代码的上限体积,可以在构建工具里跑一次 Terser 做对比;本页的价值在于即时、无需安装,且输出保留原始标识符,便于对照阅读。

为什么会保留一个换行,感觉没压干净?

那个换行是故意留下的。按 ECMAScript 的自动分号插入规则,删掉某些换行会让两行粘连成完全不同的表达式——比如 var a = b 后面紧跟 (function(){})(),删掉换行就变成把 b 当函数调用。本工具宁可多留 1 个字节也不改语义,统计条里的"保留换行处数"就是这个数量。取消勾选「必要时保留换行」可以强制删除,但请自己确认语义。

我的代码里有正则,会不会被压坏?

正则字面量会被原样保留,包括其中的空格、\s 和转义的 \/。真正的风险在"除号被误判成正则开头":判据是前一个有效字符,如果前一个字符恰好是运算符或 return 这类关键字,本工具会按正则去扫描,扫到换行或文件末尾还找不到结束的 / 就报错。这时给除法加上括号即可。

模板字符串里的缩进为什么没删?

因为模板字符串的内容是数据不是代码。反引号之间的每一个换行和空格都是字符串的一部分,删掉就等于改了运行时输出的字符串值。AST 级压缩器同样不会动模板字符串里的空白(除非启用专门的常量折叠选项),所以这不是本工具的疏漏。

报错里的「第 X 行第 Y 列」是压缩前还是压缩后的位置?

压缩前,也就是你原始输入里的位置。行号按换行符计数从 1 开始,列号是该行内的第几个字符,也从 1 开始。因为压缩会删掉大量字符,输出里的位置对不上原始代码,所以一律报原始位置。

输入只有注释时会发生什么?

压缩结果会是空字符串,不会报错。结果区会提示"压缩后没有剩余内容——输入可能只有注释",提醒你检查是不是整段都被删掉了。这种情况常见于只粘了一整段函数说明文档的片段。

代码会被传到服务器吗?

不会。整个状态机就是一个 while 循环加几个 if,没有任何网络请求,也没有引入第三方库。你可以打开浏览器开发者工具的 Network 面板确认:页面加载完之后不会再发出任何请求。

相关工具