JS 格式化
JS 格式化是把挤成一行、缩进乱掉的 JavaScript 按语法结构重排成可读代码的在线工具,用的是 prettier 官方内核。缩进 2 空格 / 4 空格 / Tab、行宽、分号、引号、尾随逗号可调,TypeScript 与 JSX/TSX 自动识别,注释、模板字符串与正则原样保留。语法有问题会用中文指出第几行第几列,全角标点会直接点名。代码在浏览器本地处理,不上传、不执行,免费免注册。
本页只把代码解析成语法树再打印回文本,不会执行你粘贴的任何一行; 注释、模板字符串与正则原样保留,但字面量写法会按上面的设置统一(详见下方对照表)。
🔒 代码在你的浏览器内处理,未上传任何服务器;本页也不执行你粘贴的代码—— 它只被解析成语法树再打印回文本,结果一律以纯文本展示,页面上没有「运行一下」这种入口。
如何使用JS 格式化
JS 怎么格式化:先在上方文本框粘贴代码,或把 .js / .mjs / .ts / .jsx / .tsx 文件拖入(浏览器本地处理、代码不上传也不执行),也可以点「填入示例」先看效果。
默认就是「自动识别 + 2 空格 + 80 列 + 带分号 + 双引号」,粘贴即出结果;按需切换语言、缩进、行宽、尾随逗号、箭头括号、换行符与分号/单引号/花括号空格三个开关,超过 100KB 会改成点「格式化」按钮再处理。
看统计条确认命中的解析器与行数字符数;语法有问题时点一下红色错误卡片能跳到输入框对应行列。确认无误后「复制」或「下载」,也可以「结果送回输入」继续处理。
关于JS 格式化的常见问题
- JS 格式化会上传我的代码吗?
- 不会。JS 格式化全程在你的浏览器本地完成,代码不上传到任何服务器,关闭页面即清除。内部脚本、带接口地址与密钥的片段、还没提交的业务代码都常见于这个场景,所以代码不上传这一点很要紧:本工具只把缩进宽度、行宽这类排版偏好写进本机 localStorage,代码正文与结果一律不落盘(页面只加载访问统计脚本,它不读取也不上报你粘贴的内容)。想验证就在内核加载过一次之后断网再排一遍,照样出结果。
- JS 格式化会执行我粘贴的代码吗?
- 不会,这是本工具的第一条红线,也是它和其它格式化工具最不一样的地方——JavaScript 是可执行语言,粘一段带 fetch 的脚本进来,只要跑一次就等于把你的浏览器数据送了出去。本页的处理路径只有一条:把代码解析成语法树,再按你选的参数打印回文本。实现里没有 eval、没有 new Function、不会把输入拼进动态 import、不会塞进任何 script 标签,页面上也没有「运行一下看看」这种入口。结果区是纯文本渲染,不走任何 HTML 解析路径。顺带说清另一面:本页也不做安全清洗,不判断代码是不是恶意的,可疑的字符串会原样透传——清洗等于改语义,和排版工具的契约冲突。
- 手机上能用 JS 格式化在线工具吗?
- 可以。本 JS 格式化在线工具是纯网页实现,手机、平板、电脑浏览器打开即用,无需安装软件,也无需注册登录。手机上排出来的结果和电脑上完全一致,输入区与结果区在窄屏会上下堆叠而不是并排,结果区支持横向滚动,长行不会被折断破坏缩进;语法错误卡片在小屏上保持完整可读,不折叠——排不出来的时候,它是唯一有用的信息。要注意的是首次使用需要下载一次格式化内核,移动网络下会有一两秒的加载态,之后同一次会话里就不用再等了。
- JS 格式化在线工具免费吗?要注册吗
- 免费,不注册、不限次数、不加水印,也没有会员档或导出条数限制。能这么给的原因很实在:格式化这件事发生在你自己的浏览器里,本站不为你的每一次排版付服务器算力,唯一的一次性成本是首次下载那份约 600KB 的内核。所以这里不会出现「每天 3 次」「超过 100KB 请升级」这种拦路条——单次 1MB 的上限是浏览器算力的边界,不是收费墙。要说代价只有一个:结果不会替你存在云端,关掉页面就没了,需要留存请点「复制」或「下载」。
- JS 格式化支持 TypeScript 和 JSX 吗?
- 支持。默认是「自动识别」:先按 JavaScript / JSX 解析,遇到类型注解、尖括号断言、satisfies、泛型箭头函数这类 TypeScript 语法会自动改用 TypeScript 解析器重排,并在结果里注明换过解析器,不会悄悄换掉。你也可以手动指定 JavaScript / JSX、TypeScript / TSX 或 Flow。装饰器、私有字段 #x、静态初始化块、顶层 await、可选链、空值合并、import 属性都能正确排版。但本页只排版不编译:不会把 TypeScript 编译成 JavaScript,不会把 JSX 转成 createElement 调用,也不做降级和 polyfill。
- JS 格式化支持 Vue 单文件组件吗
- 不支持整份 .vue、.svelte 这类单文件组件。本页的解析器只认 JavaScript / TypeScript / Flow,把一份完整的 .vue 粘进来,模板那段会被当成 JSX 读过去,读到 script 标签那一行就报语法错误——实测报的正是 script 标签所在的行。可行的做法是把 script 里的那段脚本单独粘进来排好再贴回去,模板部分交给 HTML 格式化;样式部分交给 CSS 格式化。反过来,.jsx 与 .tsx 是纯脚本文件,不是单文件组件,属于本页主场景,整份丢进来即可。
- JS 格式化用的是 prettier 内核吗?
- 是,用的是 prettier 官方发布的浏览器版内核,配上它的 babel 解析器插件,所以排版结果与 prettier 默认风格一致,团队里用 prettier 的话拿过去基本不会再产生 diff。要注意一个前提:网页读不到你项目里的配置文件,所以本页上的这几项设置就是全部配置。如果你的仓库里写了别的行宽、单引号或箭头括号策略,排出来的结果就会和 IDE 里的不一样——把页面上的选项按仓库配置对齐一遍即可。另外本页刻意不暴露实验性选项,那些选项会随版本改变默认行为,暴露出来只会让结果变得不可预期。
- JS 格式化会改动我的注释和字符串吗?
- 注释、JSDoc、行尾注释、模板字符串的内部内容、正则字面量都一个字符不改,sourceMappingURL 注释和文件开头的 hashbang 也留在原位。但有一点必须说清楚:本页做的是按语法树重新打印,不是只挪空白,所以字面量的写法会被统一——小数 .5 会补成 0.5、指数 1E3 会变小写 1e3、十六进制 0xFF 会变 0xff、引号按设置统一(同时自动选转义更少的那一种)、冗余括号会去掉、连续空行会折叠成一个。完整对照表就列在正文里,一条不藏。这也是本页不像 CSS 与 HTML 格式化那样承诺「一个字符不改」的原因。
- JS 格式化怎么让某一段代码保持原样
- 在那一段前面单起一行写 // prettier-ignore 注释,紧跟着的那条语句就会保持你原来的写法。最常见的用途是手工对齐的矩阵、坐标表、ASCII 图这类「排版本身就是信息」的代码:实测在一个分行对齐的数组前加上这行注释,它的换行与列对齐一个字符都没被动,只有语句末尾的分号仍按分号开关补齐。要是整份文件都不想被重排,那就别点格式化——本页不会自动改写输入框里的内容,结果只出现在结果区,要不要用由你决定。
- JS 格式化时提示语法错误怎么办?
- 会告诉你第几行第几列、什么原因,并给一句中文修复建议:引号没闭合、模板字符串反引号没闭合、块注释没闭合、正则没闭合、少了分号、保留字当了变量名、数字后面直接跟标识符、await 用在了非 async 函数里等等,都有对应的中文说明。点一下错误卡片就能跳到输入框对应的行列。语法不过时本页不出半成品结果,也不回退原样,输入框里的内容一个字符都不会被动——排不出来的时候,把错误位置说准比硬凑一个结果有用得多。如果错的是从聊天软件复制来的全角标点,还会直接告诉你是哪个字符、半角该写成什么。
- JS 格式化支持多大的文件?有上限吗?
- 单次上限 1MB,按 UTF-8 字节数计算,比站内其它文本工具的 2MB 更保守——因为这里要完整解析再打印两遍,单位体积的成本更高。实测桌面浏览器上 34KB 约 73 毫秒、270KB 约 273 毫秒;1MB 上限附近约 1~2.5 秒,随代码结构波动,越接近上限增长越快于线性,手机还会慢上几倍。超过 100KB 会自动关掉「边输入边排版」,改成参数调好后点按钮再处理,免得每次击键都占住主线程;超过 1MB 会直接提示并拦截,请先拆分。还有一种情况会被拒绝:嵌套层级特别深的代码(混淆产物和生成代码里常见),解析和打印都是递归的,会超出浏览器的调用栈上限,这时会给一句中文提示而不是白屏。
- JS 格式化能还原混淆过的代码吗?
- 能排出缩进和换行,读起来会比一坨压成一行的东西好很多,配合行宽设置还能把长链式调用拆开。但变量名还不回来——_0x1a2b、a、b 这些名字是在混淆的时候就已经把原名丢掉了,文件里根本没有留下原名的痕迹,任何工具都变不回去,声称能「还原」的多半是在猜。控制流平坦化、字符串数组解密这类反混淆变换本页也不做,那属于改代码而不是排版。想读懂混淆代码,正确的顺序是先在这里排开,再借助浏览器调试器逐步跟。
- JS 格式化能把代码重新排成一行吗?
- 不能,本页只做美化方向:按语法结构缩进换行、统一分号与引号,不提供把代码重新排成一行的功能,也不做变量名替换或代码混淆。原因很实际:把代码变小这件事必须结合作用域分析才安全,而且真正有效的减体积发生在构建流程里——那里能顺带做摇树、常量折叠和拆包,比在网页上单独跑一遍靠谱得多。所以本页把边界划清楚:只负责让代码变得能读。
JS 格式化用的是什么内核,能吃下哪些语言
JS 在线格式化(也就是常说的 JS 代码美化)这件事,难点从来不在缩进,而在必须先把代码真正读懂。JavaScript 里有几处天生的歧义:a/b/c 到底是两次除法还是一个正则、换行处要不要自动补分号(ASI)、 尖括号是 JSX 标签还是 TypeScript 泛型。靠词法层扫一遍空白是判不出来的——这也是 本站的 CSS 与 HTML 格式化能自己实现扫描器、而 JS 格式化必须上真正的语法分析的原因。
本页用的是 prettier 官方发布的浏览器版内核加上它的 babel 解析器插件,一份插件同时 提供了 JavaScript、TypeScript 与 Flow 三套解析器。它只在你第一次点格式化时下载一次, 不会拖累这个页面或站内其它工具页的打开速度。
| 输入类型 | 本工具怎么处理 |
|---|---|
| JavaScript(.js / .mjs / .cjs) | 主场景,含 ES2024 语法、顶层 await、动态 import |
| JSX(.jsx) | 按 JSX 排版,属性过多时自动逐行展开 |
| TypeScript(.ts / .mts / .cts) | 类型注解、interface、泛型、satisfies、装饰器都能排 |
| TSX(.tsx) | 同上,且与 JSX 的尖括号歧义由自动识别兜住 |
| Flow(// @flow) | 手动把语言切到 Flow;带 // @flow 标注的文件在自动档也能排 |
| 压成一行的构建产物 | 主场景之一,排开后配合行宽设置读长链式调用 |
| 混淆后的代码 | 能排出缩进,但变量名不还原(原名在混淆时就没了) |
| JSON | 不接管,识别到会引导去 JSON 格式化校验 |
| 本地的 prettier 配置文件 | 读不到,网页拿不到你项目里的文件;页面选项即全部配置 |
| 需要编译 / 降级 / 转译 | 不做,本页只排版不改语义 |
JS 格式化和 CSS 格式化区别:重排空白与重打印代码
这一段是本页最要紧的内容,也是同类工具页普遍讲不清的一点。本站的 XML、HTML、CSS 格式化,第一契约都是只动节点之间的空白,内容一个字符不改; 而 JS 格式化做不到这一点,也不该假装做得到。原因在实现方式: prettier 是一个 AST 重打印器——先把代码解析成语法树,再把语法树完整地重新打印一遍, 原文的排版信息在这个过程中基本被丢弃了。因此它会顺手把一批字面量写法统一掉。 这不是 bug,是设计。与其含糊其辞,不如把清单摆出来:
| 你写的 | 排版后 | 为什么 |
|---|---|---|
| const a = .5 | const a = 0.5; | 数字字面量补前导零 |
| const a = 1E3 | const a = 1e3; | 指数符号统一成小写 e |
| const a = 0xFF, b = 0xFFn | const a = 0xff, b = 0xffn; | 十六进制字面量转小写,BigInt 同理 |
| const a = '单引号' | const a = "单引号"; | 按引号设置统一(默认双引号) |
| const a = 'He said "hi"' | 保持单引号不变 | 自动选转义更少的那一种,不机械替换 |
| const a = "it's" | 保持双引号不变 | 同上,两种引号都可能被保留 |
| const a = 1(无分号) | const a = 1; | 按分号开关统一,关掉开关则一律不补 |
| const z = (1)/2/3 | const z = 1 / 2 / 3; | 不改变语义的冗余括号会被去掉 |
| const a = [1, 2,] | const a = [1, 2]; | 尾随逗号按设置统一,单行数组不补 |
| 连续两行以上的空行 | 折叠成一个空行 | prettier 的固定行为,不可配置 |
| CRLF 换行 | 按换行符设置输出 LF 或 CRLF | 整份文件统一,不混用 |
一句话记住这条分界线:HTML / CSS / XML 格式化是「重排空白」,JS 格式化是「重打印代码」——前者能承诺一个字符 不改,后者只能承诺语义不变、写法会被统一。反过来说,真正属于「内容」的东西一个都不会动:注释与 JSDoc 内部逐字符原样、 行尾注释跟着它所属的那条语句走、模板字符串里的换行与缩进一个字符不动、 正则字面量不会被当成除号切开、// prettier-ignore 标注的那一段维持原写法、文件开头的 hashbang 与 BOM 都留在原位。
JS 格式化前后对比示例:一段没有分号的取数代码
页面里「填入示例」用的就是下面这段代码,它一次踩满了中文行注释、无分号的 ASI 断句、 单行 if、模板字符串里的 ${} 与斜杠、除号与正则的歧义、行尾注释的归属、可选链与 空值合并、多行模板字符串八个容易被改坏的点:
// 商品详情:带重试的取数
const cfg={retry:3,base:"https://a.com/v1"}
async function load(id,{signal}={}){if(!id)return null
const r=await fetch(`${cfg.base}/item/${id}`,{signal})
if(!r.ok)throw new Error(`HTTP ${r.status}`)
const d=await r.json();return d?.item??null}
const ratio=total/count/2,re=/\/api\/v\d+/g // 除号与正则要分清
export default{load,ratio,re,tip:`第一行
第二行`}输入是 9 行 / 360 字符 / 410 字节。按默认选项(自动识别 / 2 空格 / 80 列 / 带分号 / 双引号 / 尾随逗号全补)美化后,实测输出是 18 行 / 434 字符 / 484 字节,命中的解析器是 JavaScript / JSX:
// 商品详情:带重试的取数
const cfg = { retry: 3, base: "https://a.com/v1" };
async function load(id, { signal } = {}) {
if (!id) return null;
const r = await fetch(`${cfg.base}/item/${id}`, { signal });
if (!r.ok) throw new Error(`HTTP ${r.status}`);
const d = await r.json();
return d?.item ?? null;
}
const ratio = total / count / 2,
re = /\/api\/v\d+/g; // 除号与正则要分清
export default {
load,
ratio,
re,
tip: `第一行
第二行`,
};八处细节值得对照着看:中文行注释原样保留;原文一个分号都没有,靠 ASI 正确断成了六条语句并统一补上分号;if (!id) return null 这种单行 if 没有被强行拆成三行;模板字符串里的 ${} 与那个斜杠没被当成除号; 同一行里的 total / count / 2 判成了除法、/\/api\/v\d+/g 判成了正则;行尾注释跟着 re 那一条走,没有被挪到别处; 可选链与空值合并正常;多行模板字符串内部的换行与那两个空格缩进逐字符原样。 这份结果还是幂等的——把它再格式化一次,输出与现在逐字节相同, 所以「结果送回输入」这个按钮点多少次都不会越排越歪。
JS 格式化报语法错误时,第几行第几列怎么读
JS 和 CSS、HTML 不一样:后两者没有「良构」这个契约,浏览器遇到看不懂的声明是跳过继续, 所以那两个工具永不拒绝出结果。JS 语法不过就是真的排不出来——语法树都建不起来, 自然也就没法打印。这时本页的目标从「给结果」变成「让你三秒内知道错在哪一行」,所以错误卡片里给的是行、列、 中文原因、修复建议和出错行原文,点一下还能跳到输入框对应位置。语法不过时结果区是空的, 不出半成品也不回退原样,输入框内容也一个字符不动。
中文说明不是把英文报错直译过来的,而是按解析器给出的稳定错误码查表得到的—— 直译「Unexpected token」对中文用户毫无帮助。下面是完整对照表:
| 触发写法 | 中文原因 | 怎么修 |
|---|---|---|
| const s = "abc | 字符串的引号没有闭合 | 检查引号是否成对,跨行文本改用反引号 |
| const s = `abc | 模板字符串的反引号没有闭合 | 补上结尾的反引号 |
| /* abc | 块注释没有闭合 | 补上 */,否则往后的代码全被当注释 |
| const r = /abc | 正则字面量没有闭合 | 补上结尾的 /;本意是除号就两边加空格 |
| const a=1 const b=2 | 少了分号或换行,两条语句连在了一起 | 在此处断句 |
| const class = 1 | 用了保留字当名字 | 换一个不是保留字的名字 |
| const a = 1abc | 数字后面直接跟了标识符 | 检查是否漏了运算符或引号 |
| function a(){ return await b } | await 只能用在 async 函数或模块顶层 | 给外层函数加 async |
| delete this.#x | 类的私有字段不能被 delete | 改成赋值 undefined 或换普通属性 |
| const a = <string>x(按 JS 解析) | 既像 JSX 标签又像 TS 类型断言 | 把语言切到 TypeScript |
| 括号 / 大括号没配对 | 这里出现了不该出现的符号 | 检查本行与上一行的各类括号与逗号 |
| 嵌套上百层的调用或数组 | 嵌套层级过深,超出浏览器处理上限 | 拆分后重试 |
| 解析器给出的新错误码 | 这一行的语法有问题 | 兜底文案,永不吐英文原文或堆栈 |
JS 格式化的自动识别是怎么判 TypeScript 的
默认的「自动识别」不是靠猜文件名,也不是靠扫关键字,而是先真解析一遍: 先按 JavaScript / JSX 解析,成功就用它的结果;失败了再用 TypeScript 解析器重试一次, 成功就用后者,并在结果里明确写一句「已改用 TypeScript 解析器」——不会悄悄换掉, 因为这两者在少数写法上的读法确实不同。
顺序为什么是「JavaScript 先、TypeScript 兜底」而不是反过来?因为尖括号在两套语法里 都有意义,谁先谁就在歧义处胜出,而 JSX 是 Web 前端更常见的场景。下面这张表是实测结论:
| 写法 | JavaScript 解析器 | TypeScript 解析器 | 结论 |
|---|---|---|---|
| const a: number = 1 | 解析失败 | 成功 | 必须靠兜底 |
| const a = <string>x(类型断言) | 解析失败 | 成功 | 必须靠兜底 |
| const id = <T,>(x: T): T => x | 解析失败 | 成功 | 必须靠兜底 |
| const a = {x:1} satisfies R | 解析失败 | 成功 | 必须靠兜底 |
| const a = <div>{x}</div>(JSX) | 成功 | 成功且输出一致 | 谁先都行,保留 JSX 读法 |
| const a = 1; function g(){} | 成功 | 成功且输出一致 | 谁先都行 |
| class A{@dec m(){}}(装饰器) | 成功 | 成功 | 谁先都行 |
也就是说:JavaScript 解析器能过的,TypeScript 解析器基本也能过且输出相同; 前者过不了的,后者大概率能过。所以这个顺序既保住了 JSX 的读法,又不用让 TypeScript 用户手动切档。当然,你随时可以手动指定语言——手动指定时就不再兜底, 选了 JavaScript 却粘了 TypeScript 代码,会直接告诉你哪一行不认识。
还有一类失败解析器帮不上忙:粘错了工具。本页会看输入开头的特征给出 下一步入口——像 JSON 对象就引导去 JSON 格式化校验,像 CSS 就引导去 CSS 格式化, 像 HTML 就引导去 HTML 格式化。其中 HTML 片段这条最隐蔽:<div class="a">x</div> 会被当成 JSX 解析成功,只是末尾多出一个分号,用户完全看不出异常,所以这条提示在成功路径上也会给。这些提示只是提示,永远不改变是否格式化,也不改变输出。
JS 格式化能做什么、不能做什么
先给一组桌面 Chromium 上的实测耗时,挑不挑要拆分心里有个数:34KB 约 73ms、270KB 约 273ms,1MB 上限附近约 1~2.5 秒, 百 KB 以内接近线性,越靠近上限越快于线性、也越随代码结构波动(手机再慢上几倍)。所以 100KB 这条「关掉边输入边排版」的线 对应的正是两三百毫秒——再往上,每敲一个字符就要占住主线程这么久,打字会明显发涩。 另外首次格式化要先下载一次约 600KB 的内核,那一两秒的加载态与代码体积无关, 同一次会话里只发生一次。