一、核心区别

  贪婪(Greedy) 惰性(Lazy)
写法 *+?{n,m} 量词后加 ?*?+???{n,m}?
策略 先吃撑,再吐出来 — 一口吞下尽可能多的字符,匹配不了再一点点往回退 先饿着,再多吃 — 先匹配尽可能少的字符,不够再一个个往后加
结果 匹配最长的结果 匹配最短的结果

比喻

  • 贪婪:巴蛇吞象——一口吞下整个字符串,发现匹配不了,再从后面一点点吐出来,直到能匹配为止
  • 惰性:太懒了——先吃一个字符,如果不匹配再多添一个,够了就停

二、经典示例

对字符串 "aa{b{aa}bb{aaa}b}aa" 匹配花括号内容:

var re1 = /\{.*\}/g;    // 贪婪
var re2 = /\{.*?\}/g;   // 惰性

"aa{b{aa}bb{aaa}b}aa".match(re1);
// → ["{b{aa}bb{aaa}b}"]  ← 匹配了最外层的一整个大块

"aa{b{aa}bb{aaa}b}aa".match(re2);
// → ["{b{aa}", "{aaa}"]  ← 匹配了两个最短的片段

匹配过程图解

贪婪 \{.*\}

{b{aa}bb{aaa}b}aa
^                    ← 遇到 { 开始匹配
{b{aa}bb{aaa}b}aa
 ^^^^^^^^^^^^^^^^^   ← .* 一口吞到字符串末尾
{b{aa}bb{aaa}b}aa
                ^^   ← 末尾不是 },往回吐
{b{aa}bb{aaa}b}aa
               ^     ← 找到 },匹配成功 → "{b{aa}bb{aaa}b}"

惰性 \{.*?\}

{b{aa}bb{aaa}b}aa
^                    ← 遇到 { 开始匹配
{b{aa}bb{aaa}b}aa
 ^                   ← .*? 先不吃,看下一个是不是 } → 不是(b),多吃一个
{b{aa}bb{aaa}b}aa
  ^                  ← 下一个是 {,不是 } → 多吃一个
...
{b{aa}bb{aaa}b}aa
     ^               ← 下一个是 },匹配成功 → "{b{aa}"
                     ← 继续找下一个匹配 → "{aaa}"

三、所有贪婪量词与惰性对照

贪婪 惰性 含义(贪婪) 含义(惰性)
* *? 0 次或多次,尽量多 0 次或多次,尽量少
+ +? 1 次或多次,尽量多 1 次或多次,尽量少
? ?? 0 次或 1 次,优先 1 次 0 次或 1 次,优先 0 次
{n,m} {n,m}? n 到 m 次,尽量多(趋向 m) n 到 m 次,尽量少(趋向 n)
{n,} {n,}? 至少 n 次,尽量多 至少 n 次,尽量少

四、实际应用

提取 HTML 标签

var html = '<div>hello</div><span>world</span>';

html.match(/<.*>/g);
// 贪婪 → ["<div>hello</div><span>world</span>"]  ← 整段吞掉

html.match(/<.*?>/g);
// 惰性 → ["<div>", "</div>", "<span>", "</span>"]  ← 逐个标签

提取引号内内容

var text = '他说"你好",她说"再见"';

text.match(/".*"/g);
// 贪婪 → ['"你好",她说"再见"']  ← 从第一个引号吃到最后一个引号

text.match(/".*?"/g);
// 惰性 → ['"你好"', '"再见"']  ← 分别匹配

经验法则 大部分情况下,如果你想匹配成对的分隔符之间的内容(引号、括号、标签等),用惰性匹配更安全。或者更精确的做法是用排除字符集,比如 "[^"]*" 匹配引号内容,完全不需要纠结贪婪还是惰性。