正則表達式是文字處理的瑞士刀。JScript (Microsoft 的 ECMAScript 3 實作) 提供 `RegExp` 物件與 `String` 相關方法,雖然語法與現代 JavaScript (ES5+) 大同小異,但仍有獨特屬性 (如靜態 `RegExp.$1`、$`、$' 等)。本文系統整理建立、匹配、替換、屬性存取等核心用法,並展示語法高亮、樣板引擎等實戰模式。
// 1. 建構函式 (需雙重跳脫)
var re1 = new RegExp("\\w+", "igm"); // 字串中 \ 需寫 \\
// 2. 字面量語法 (推薦,可讀性高、無需跳脫)
var re2 = /\w+/igm;
旗標:`i` (忽略大小寫)、`g` (全域匹配)、`m` (多行模式 `^$` 匹配行首尾)。
| 方法 | 說明 | 回傳值 |
|---|---|---|
exec(str) | 執行匹配,**可迭代** (配合 `g` 旗標),每次從 `lastIndex` 開始 | 匹配陣列 (含 index、input、groups) 或 null |
test(str) | 僅檢查是否匹配 | true / false |
compile(pattern, flags) | 預編譯正則 (舊版優化),現代引擎自動優化,**不建議使用** | RegExp 物件本身 |
var re = /\w+/g;
var str = "Hello World 123";
var match;
while ((match = re.exec(str)) !== null) {
console.log("匹配:", match[0], "位置:", match.index, "下一起始:", re.lastIndex);
}
// 輸出:
// 匹配: Hello 位置: 0 下一起始: 5
// 匹配: World 位置: 6 下一起始: 11
// 匹配: 123 位置: 12 下一起始: 15
執行 `exec()` 或 `test()` 後,`RegExp` 建構函式本身會更新下列**靜態屬性** (全域共享,非執行緒安全):
| 屬性 | 說明 |
|---|---|
RegExp.$1 ~ RegExp.$9 | 最近一次匹配的第 1~9 組捕獲群組內容 |
RegExp.lastMatch / RegExp['$&'] | 最近一次匹配的完整字串 |
RegExp.lastParen / RegExp['$+'] | 最近一次匹配的**最後一組**捕獲群組內容 |
RegExp.leftContext / RegExp['$`'] | 目標字串中匹配位置**之前**的所有內容 |
RegExp.rightContext / RegExp["$'"] | 目標字串中匹配位置**之後**的所有內容 |
RegExp.input / RegExp['$_'] | 最近一次傳入 `exec/test` 的目標字串 |
RegExp.lastIndex | 下一次匹配起始位置 (同實例屬性) |
| 方法 | 說明 | 關鍵差異 |
|---|---|---|
match(regexp) | 匹配字串 | 無 `g` 時同 `exec`;有 `g` 時回傳**所有匹配字串陣列** (無捕獲群組資訊) |
replace(regexp, replacement) | 替換匹配內容 | **支援函式回呼** 與**特殊替換變數** (見下) |
search(regexp) | 搜尋首個匹配位置 | 回傳索引或 -1,忽略 `g` 旗標 |
split(regexp, limit) | 分割字串 | 分隔符可為正則 |
第二參數為字串時,支援下列**特殊變數** (類似 Perl):
| 變數 | 代表內容 |
|---|---|
$& | 完整匹配字串 (同 `lastMatch`) |
$1 ~ $99 | 第 1~99 組捕獲群組 |
$` | 左側文本 (`leftContext`) |
$' | 右側文本 (`rightContext`) |
$$ | 字面 `$` 字元 |
var code = "function hello() { return 'world'; }";
var keywords = /\b(function|return|var|if|else|for|while)\b/g;
// 替換為 關鍵字
var highlighted = code.replace(keywords, '<span class="kw">$&</span>');
console.log(highlighted);
// 輸出: function hello() { return 'world'; }
// 將所有數字乘 2
var text = "價格: 10, 數量: 3, 總計: 30";
var result = text.replace(/\d+/g, function(match, offset, string) {
return parseInt(match) * 2;
});
console.log(result); // "價格: 20, 數量: 6, 總計: 60"
// 更複雜:解析模板 {{name}} → 資料對應值
var template = "你好, {{user}}! 你的等級: {{level}}";
var data = { user: "小明", level: "VIP" };
var rendered = template.replace(/\{\{(\w+)\}\}/g, function(match, key) {
return data[key] || match; // 找不到保留原樣
});
console.log(rendered); // "你好, 小明! 你的等級: VIP"
<html><body>
<textarea id="codeInput" rows="15" cols="80">
function factorial(n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
</textarea>
<button onclick="highlight()">高亮顯示</button>
<pre id="output" style="border:1px solid #ccc; padding:10px; background:#f8f8f8;"></pre>
<script>
function highlight() {
var code = document.getElementById("codeInput").value;
// HTML 編碼防 XSS
code = code.replace(/&/g, "&").replace(//g, ">");
// 定義規則陣列:[正則, CSS類別]
var rules = [
[/\b(function|return|var|if|else|for|while|do|break|continue|switch|case|default|try|catch|finally|throw|new|this|typeof|instanceof|in|delete|void)\b/g, "kw"],
[/\b(true|false|null|undefined|NaN|Infinity)\b/g, "literal"],
[/\b\d+(\.\d+)?\b/g, "number"],
[/"([^"\\]|\\.)*"/g, "string"],
[/'([^'\\]|\\.)*'/g, "string"],
[/\/\/.*$/gm, "comment"],
[/\/\*[\s\S]*?\*\//g, "comment"]
];
// 為避免標籤巢狀破壞,採用「佔位符法」:先替換為唯一標記,最後一次性轉換
var placeholders = [];
rules.forEach(function(rule, i) {
var regex = rule[0], cls = rule[1];
code = code.replace(regex, function(match) {
var ph = "@@PH" + i + "_" + placeholders.length + "@@";
placeholders.push({ ph: ph, html: '<span class="' + cls + '">' + match + '</span>' });
return ph;
});
});
// 還原佔位符
placeholders.forEach(function(p) {
code = code.replace(p.ph, p.html);
});
document.getElementById("output").innerHTML = code;
}
</script>
<style>
.kw { color: #0000ff; font-weight: bold; }
.literal { color: #008000; }
.number { color: #ff0000; }
.string { color: #800080; }
.comment { color: #008000; font-style: italic; }
</style>
</body></html>