压缩的核心原理其实主要是三个
- 移除注释
- 缩短标识符
- 压缩空白符
一、移除注释
js
// 1. 移除注释
let result = code
.replace(/\/\*[\s\S]*?\*\//g, '') // 多行注释
.replace(/\/\/.*$/gm, ''); // 单行注释
代码解释
这段代码是用来移除 JavaScript 代码中的注释的,使用了两个正则表达式替换:
-
第一个
.replace()
移除多行注释(/* ... */
):- 正则表达式:
/\/\*\s\S*?\*\//g
- 解释:
\/\*
匹配/*
开始符号\s\S*?
非贪婪匹配任意字符(包括换行)\*\/
匹配*/
结束符号
g
标志表示全局匹配
- 正则表达式:
-
第二个
.replace()
移除单行注释(// ...
):- 正则表达式:
/\/\/.*$/gm
- 解释:
\/\/
匹配//
.*
匹配后面的所有字符直到行尾$
匹配行尾
gm
标志表示全局和多行匹配
- 正则表达式:
示例: javascript
js
const code = `
// 单行注释
function test() {
/* 多行
注释 */
return 1;
}
`;
const result = code
.replace(/\/\*\s\S*?\*\//g, '')
.replace(/\/\/.*$/gm, '');
console.log(result);
/* 输出:
function test() {
return 1;
}
*/
/s和/S分别表示
在正则表达式中,\s
和 \S
是两个常用的元字符,它们分别表示:
\s
(小写 s)
- 含义:匹配任何空白字符(whitespace character)
- 包括:
- 空格
- 制表符
\t
- 换行符
\n
- 回车符
\r
- 垂直制表符
\v
- 换页符
\f
- 空格
\S
(大写 S)
- 含义:匹配任何非空白字符(non-whitespace character)
- 相当于:
^\s
(即不是\s
的字符)
示例对比 javascript
js
const str = "Hello\tWorld\n123";
// 匹配所有空白字符
console.log(str.match(/\s/g));
// 输出: "\t", "\n" (制表符和换行符)
// 匹配所有非空白字符
console.log(str.match(/\S/g));
// 输出: "H", "e", "l", "l", "o", "W", "o", "r", "l", "d", "1", "2", "3"
在原始问题中的应用
在最初的代码中: javascript
js
.replace(/\/\*\s\S*?\*\//g, '')
- `\s\S` 是一个巧妙用法,表示匹配所有字符(包括换行符):
- `\s` 匹配空白字符(如换行符)
- `\S` 匹配非空白字符
- 组合起来等价于 `.`(点号)但能匹配换行符(因为普通的 `.` 默认不匹配换行符)
这种写法比 /\/\*.*?\*\//gs
(使用 s
标志让 .
匹配换行符)更兼容旧版 JavaScript 环境。
[\s\S]*? 非贪婪匹配是什么
非贪婪匹配(Lazy/Lenient Matching) 在正则表达式中,贪婪匹配(Greedy Matching) 和 非贪婪匹配(Lazy Matching) 是两种不同的匹配策略:
- 贪婪匹配(默认):尽可能匹配最长的字符串。
- 非贪婪匹配:尽可能匹配最短的字符串。
- 如何表示非贪婪匹配?
在量词(*
, +
, ?
, {n,m}
)后面加上 ?
,就变成了非贪婪模式:
量词 | 贪婪模式 | 非贪婪模式 |
---|---|---|
* |
.* |
.*? |
+ |
.+ |
.+? |
? |
?? (极少用) |
?? (极少用) |
{n,m} |
{n,m} |
{n,m}? |
- 示例对比 示例 1:匹配 HTML 标签
js
html
<div>Hello</div><div>World</div>
(1) 贪婪匹配(`<div>.*</div>`)
javascript
"<div>Hello</div><div>World</div>".match(/<div>.*<\/div>/);
// 匹配整个字符串:
// "<div>Hello</div><div>World</div>"
(2) 非贪婪匹配(`<div>.*?</div>`)
javascript
"<div>Hello</div><div>World</div>".match(/<div>.*?<\/div>/);
// 只匹配第一个 `<div>`:
// "<div>Hello</div>"
示例 2:移除多行注释 在最初的代码中: javascript
js
.replace(/\/\*\s\S*?\*\//g, '')
- `\s\S*?` 表示非贪婪匹配任意字符(包括换行),直到遇到第一个 `*/`。
- 如果不加 `?`(贪婪模式),它会匹配从第一个 `/*` 到最后一个 `*/` 之间的所有内容,可能会错误地吃掉多个注释块。
- 何时使用非贪婪匹配?
✅ 适用场景:
- 提取最短匹配的内容(如 HTML/XML 标签、注释块)。
- 避免匹配过多内容(如
.*
默认会匹配到最后一个符合条件的字符)。
❌ 不适用场景:
- 需要匹配到最后一个符合条件的字符时(如提取整个 JSON 字符串)。
- 总结 模式 写法 行为
贪婪匹配 .*
, .+
, {n,m}
尽可能匹配最长字符串 非贪婪匹配 .*?
, .+?
, {n,m}?
尽可能匹配最短字符串
在写正则表达式时,如果发现匹配了过多内容,可以尝试使用 ?
改成非贪婪模式。
二、缩短标识符(基本版)
js
const varMap = new Map();
let varCount = 0; // 匹配变量声明
result = result.replace( /\b(var|let|const|function)\s+([a-zA-Z_$][\w$]+)/g,
(match, keyword, varName) => {
if (!varMap.has(varName)) {
varMap.set(varName, `v${varCount++}`);
}
return `${keyword} ${varMap.get(varName)}`;
} );
代码解析
这段代码的作用是匹配 JavaScript 变量声明(var
/let
/const
/function
),并将变量名替换成一个简短的、自动生成的名称(如 v0
, v1
, v2
...),通常用于代码混淆(Obfuscation)或缩小变量名的场景。
- 正则表达式 javascript
js
/\b(varletconstfunction)\s+(a-zA-Z_$\w$+)/g
- `\b`:匹配单词边界(防止匹配到类似 `myvar` 这样的单词)。
- `(var let const function)`:匹配变量声明关键字(`var`、`let`、`const` 或 `function`)。
- `\s+`:匹配至少一个空白字符(如空格、制表符)。
- `(a-zA-Z_$\w$+)`:匹配变量名:
- `a-zA-Z_$`:变量名必须以字母、`_` 或 `$` 开头。
- `\w$+`:后续字符可以是字母、数字、`_` 或 `$`。
- 替换函数 javascript
js
(match, keyword, varName) => {
if (!varMap.has(varName)) {
varMap.set(varName, `v${varCount++}`);
}
return `${keyword} ${varMap.get(varName)}`;
}
- 参数:
- `match`:整个匹配到的字符串(如 `let myVar`)。
- `keyword`:匹配到的关键字(如 `let`)。
- `varName`:匹配到的变量名(如 `myVar`)。
- 逻辑:
1. 检查 `varMap`(可能是一个 `Map` 或对象)是否已存储该变量名。
2. 如果没有,则生成一个新名称(如 `v0`, `v1`),并存入 `varMap`。
3. 返回替换后的字符串(如 `let v0`)。
示例 输入代码
js
let myVar = 10;
const myConst = "Hello";
function myFunc() {}
处理后
let v0 = 10;
const v1 = "Hello";
function v2() {}
(假设 `varCount` 从 `0` 开始)
三、压缩空白符
js
// 3. 压缩空白符
result = result
.replace(/\s+/g, ' ') // 多空格合并
.replace(/\s*([={}[\](),;:])\s*/g, '$1') // 清除运算符周围空格
.replace(/;}/g, '}'); // 结尾分号清除
return result;
}
这段代码的作用是压缩 JavaScript 代码中的空白符,以减少文件大小并提高加载速度。它主要做了三件事:
- 合并多个连续空格为单个空格
- 清除运算符和符号周围不必要的空格
- 移除
;}
中的分号(因为}
前不需要分号)
代码解析
replace(/\s+/g, ' ')
- 正则表达式:
/\s+/g
\s+
:匹配一个或多个空白字符(包括空格、换行符、制表符等)。
- 替换:
' '
- 将所有连续的空白符替换成单个空格。
示例: javascript
js
"Hello World\n\nGoodbye" → "Hello World Goodbye"
replace(/\s*(,;:)\s*/g, '$1')
- 正则表达式:
/\s*(,;:)\s*/g
\s*
:匹配零个或多个空白字符(可选)。(,;:)
:匹配以下符号:=
(赋值){}
(大括号、方括号、圆括号),;:
(逗号、分号、冒号)
\s*
:再次匹配零个或多个空白字符(可选)。
- 替换:
'$1'
- 只保留符号本身,删除其前后的所有空格。
示例: javascript
js
"let x = 1 ;" → "let x=1;"
"if ( x === y ) {" → "if(x===y){"
replace(/;}/g, '}')
- 正则表达式:
/;}/g
- 匹配
;}
(分号后紧跟右大括号)。
- 匹配
- 替换:
'}'
- 删除
}
前的分号(因为 JavaScript 允许}
前不加分号)。
- 删除
示例: javascript
js
"function test() { return 1; }" → "function test(){return 1}"
完整示例
js
javascript
function test ( ) {
let x = 1 ;
if ( x === 2 ) {
return "Hello" ;
}
}
处理后
javascript
js
function test(){let x=1;if(x===2){return "Hello"}}