Assertions
==========

断言的组成之一是边界。对于文本、词或模式，边界可以用来表明它们的起始或终止部分（如先行断言，后行断言以及条件表达式）。

尝试一下
----

类型
--

### 边界类断言

| 字符 | 含义 |
| --- | --- |
| `^` | 
 匹配输入的开头。如果多行模式设为 true，`^`
 在换行符后也能立即匹配，比如 `/^A/` 匹配不了 "an A" 里面的
 "A"，但是可以匹配 "An A" 里面第一个 "A"。


**备注：**`^` 出现在集合或范围开头时的含义与此不同（参见
 group）。

 |
| `$` | 
 匹配输入的结束。如果多行模式设为 true，`$`
 在换行符前也能立即匹配，比如 `/t$/` 不能匹配 "eater" 中的
 "t"，但是可以匹配 "eat" 中的 "t"。
  |
| `\b` | 匹配一个单词的边界，这是一个字的字符前后没有另一个字的字符位置，例如在字母和空格之间。需要注意的是匹配的单词边界不包括在匹配中。换句话说，匹配字边界的长度为零。
一些例子：* `/\bm/` 在 "moon" 中匹配到 "m"
* `/oo\b/` 在 "moon" 中不会匹配到 "oo", 因为 "oo" 后面跟着
 "n" 这个单词字符。
* `/oon\b/` 在 "moon" 中匹配 "oon"，因为 "oon"
 是这个字符串的结尾，因此后面没有单词字符
* `/\w\b\w/`
 将永远不会匹配任何东西，因为一个单词字符后面永远不会同时有一个非单词字符和一个单词字符。

 匹配退格字符 (`[\b]`), 查看
 字符类
 |
| `\B` | 
 匹配非单词边界。这是上一个字符和下一个字符属于同一类型的位置：要么两者都必须是单词，要么两者都必须是非单词，例如在两个字母之间或两个空格之间。字符串的开头和结尾被视为非单词。与匹配的词边界相同，匹配的非词边界也不包含在匹配中。例如，`/\Bon/`
 在“at noon”中匹配“on” ，`/ye\B/` 在 "possibly
 yesterday"中匹配"ye" 。
  |

### 其他断言

**备注：** `？`字符也可用作量词


| `字符` | `含义` |
| --- | --- |
| `x(?=y)` | 

|  |
| --- |
| **先行断言：** x 被 y 跟随时匹配
 x。例如，对于/`Jack(?=Sprat)`/，“Jack”在跟有“Sprat”的情况下才会得到匹配．`/Jack(?=Sprat|Frost)/`
 “Jack”后跟有“Sprat”或“Frost”的情况下才会得到匹配。不过，
 匹配结果不包括“Sprat”或“Frost”。
  |

 |
| x(?!y) | 

|  |
| --- |
| **先行否定断言：** x 没有被 y 紧随时匹配
 x。例如，对于`/\d+(?!\.)/`，数字后没有跟随小数点的情况下才会得到匹配。对于`/\d+(?!\.)/.exec(3.141)`，匹配‘141’而不是‘3’。
  |

 |
| (?<=y)x | 

|  |
| --- |
| **后行断言：** x 跟随 y 的情况下匹配
 x。例如，对于`/(?<=Jack)Sprat/`，“Sprat”紧随“Jack”时才会得到匹配。对于`/(?<=Jack|Tom)Sprat`，“Sprat”在紧随“Jack”或“Tom”的情况下才会得到匹配。不过，匹配结果中不包括“Jack”或“Tom”。
  |

 |
| (?<!y)x | 

|  |
| --- |
| **后行否定断言：** x 不跟随 y 时匹配
 x。例如，对于`/(?<!-)\d+/`，数字不紧随 -
 符号的情况下才会得到匹配。对于`/(?<!-)\d+/.exec(3)`
 ，“3”得到匹配。
 而`/(?<!-)\d+/.exec(-3)`的结果无匹配，这是由于数字之前有
 - 符号。
  |

 |

示例
--

### 一般边界类型概述示例
```

// 使用 正则表达式边界修复错误字符串
buggyMultiline = `tey, ihe light-greon apple
tangs on ihe greon traa`;

// 1) 使用 ^ 修正字符串开始处和换行后的匹配。
buggyMultiline = buggyMultiline.replace(/^t/gim,'h');
console.log(1, buggyMultiline); // 修复 'tey'=>'hey'(字符串开始) , 'tangs'=>'hangs'(换行后)

// 2) 使用 $ 修正字符串结尾处的匹配。
buggyMultiline = buggyMultiline.replace(/aa$/gim,'ee.');
console.log(2, buggyMultiline); // 修复 'traa' => 'tree'.

// 3) 使用 \b 修正单词和空格边界上的字符。
buggyMultiline = buggyMultiline.replace(/\bi/gim,'t');
console.log(3, buggyMultiline); // 修复 'ihe' => 'the'  不影响 'light'.

// 4) 使用 \B 匹配实体边界内的字符。
fixedMultiline = buggyMultiline.replace(/\Bo/gim,'e');
console.log(4, fixedMultiline); // 修复  'greon'  不影响'on'.
```

### 使用 ^（控制字符）匹配输入的开头

使用 `^`匹配输入的开头。在这个例子中，我们可以通过 /^A/ 正则表达式得到以 A 开头的水果。为了选择合适的水果，我们可以使用带有箭头函数的过滤方法。
```

let fruits = ["Apple", "Watermelon", "Orange", "Avocado", "Strawberry"];

// 使用正则 /^A/ 选择以'A'开头的水果。
// 这里的 '^' 只有一种含义：匹配输入的开头。

let fruitsStartsWithA = fruits.filter(fruit => /^A/.test(fruit));
console.log(fruitsStartsWithA); // [ 'Apple', 'Avocado' ]
```


在第二个示例中，^用于在输入的开始处匹配，以及在内部使用时用于创建否定或被补充的字符集组和范围。
```

let fruits = ["Apple", "Watermelon", "Orange", "Avocado", "Strawberry"];

// 使用正则 /^[^A]/ 选择 不是以‘A’开头的水果
// 在这个例子中，“^”控件符号表示两种含义：
// 1) 匹配输入的开头
// 2) 一个否定的字符集：[^A] ，意思是匹配不是‘A’的字符

let fruitsStartsWithNotA = fruits.filter(fruit => /^[^A]/.test(fruit));

console.log(fruitsStartsWithNotA); // [ 'Watermelon', 'Orange', 'Strawberry' ]
```

### 匹配字边界
```

let fruitsWithDescription = ["Red apple", "Orange orange", "Green Avocado"];

// 选择包含以“en”或“ed”结尾的单词的描述：
let enEdSelection = fruitsWithDescription.filter(descr => /(en|ed)\b/.test(descr));

console.log(enEdSelection); // [ 'Red apple', 'Green Avocado' ]
```

### 先行断言
```

// JS 先行断言 x(?=y) 匹配被 y 跟随的 x

let regex = /First(?= test)/g;

console.log('First test'.match(regex)); // [ 'First' ]
console.log('test First peach'.match(regex)); // null
console.log('This is a First test in a year.'.match(regex)); // [ 'First' ]
console.log('This is a First peach in a month.'.match(regex)); // null
```

### 先行否定断言

例如， `/\d+(?!\.)/` 匹配没有被小数点跟随且至少有一位的数字。 `/\d+(?!\.)/.exec('3.141')` 匹配 "141" 而不是 "3"
```

console.log(/\d+(?!\.)/g.exec('3.141')); // [ '141', index: 2, input: '3.141' ]
```

### 不同含义的'?!'：断言和范围的组合用法

不同含义的 `?!` 结合使用断言 `/x(?!y)/` 和范围 `[^?!]`。
```

let orangeNotLemon = "Do you want to have an orange? Yes, I do not want to have a lemon!";

let selectNotLemonRegex = /[^?!]+have(?! a lemon)[^?!]+[?!]/gi
console.log(orangeNotLemon.match(selectNotLemonRegex)); // [ 'Do you want to have an orange?' ]

let selectNotOrangeRegex = /[^?!]+have(?! an orange)[^?!]+[?!]/gi
console.log(orangeNotLemon.match(selectNotOrangeRegex)); // [ ' Yes, I do not want to have a lemon!' ]
```

### 后行断言
```

let oranges = ['ripe orange A ', 'green orange B', 'ripe orange C',];

let ripe_oranges = oranges.filter( fruit => fruit.match(/(?<=ripe )orange/));
console.log(ripe_oranges); // [ 'ripe orange A ', 'ripe orange C' ]
```

参见
--

* 正则表达式指南
    + 字符类
    + 量词
    + Unicode 属性转义
    + 组和范围
* RegExp() 构造函数