Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[찬우] proposal-string-matchAll #28

Open
wants to merge 41 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
3900c88
ko : proposal-object-getownpropertydescriptors 번역 완료
Mar 14, 2023
2ccb500
summary : proposal-object-getownpropertydescriptors 요약 1차 완료
chanuuuuu Mar 15, 2023
b799a26
Merge branch 'main' into main
chanuuuuu Mar 15, 2023
cc5f6e9
Merge branch '11st-corp:main' into main
chanuuuuu Mar 18, 2023
85c49b7
summary : proposal-object-getownpropertydescriptors proto 관련 내용 추가
chanuuuuu Mar 18, 2023
8ee2e0f
summary : proposal-object-getownpropertydescriptors appendix 추가
chanuuuuu Mar 18, 2023
8ab1f70
summary : proposal-object-getownpropertydescriptors appendix 수정
chanuuuuu Mar 18, 2023
8ab4025
summary : proposal-object-getownpropertydescriptors appendix Object.a…
chanuuuuu Mar 20, 2023
2a38a08
ko : proposal-dotAll-flag-for-reglar-expressions
chanuuuuu Mar 20, 2023
193181c
delete : proposal-dotAll-flag-for-reglar-expressions branch 분리를 위한 삭제
chanuuuuu Mar 20, 2023
4a21a62
Merge branch 'main' into main
chanuuuuu Mar 22, 2023
1104c24
Update src/ko/proposal-object-getownpropertydescriptors.md
chanuuuuu Mar 22, 2023
d855099
Update src/ko/proposal-object-getownpropertydescriptors.md
chanuuuuu Mar 22, 2023
f98c490
Update src/ko/proposal-object-getownpropertydescriptors.md
chanuuuuu Mar 22, 2023
46c1716
Update src/ko/proposal-object-getownpropertydescriptors.md
chanuuuuu Mar 22, 2023
f59b405
Update src/ko/proposal-object-getownpropertydescriptors.md
chanuuuuu Mar 22, 2023
f0ba809
Update src/ko/proposal-object-getownpropertydescriptors.md
chanuuuuu Mar 22, 2023
9440594
Update src/ko/proposal-object-getownpropertydescriptors.md
chanuuuuu Mar 22, 2023
65d4860
Update src/ko/proposal-object-getownpropertydescriptors.md
chanuuuuu Mar 22, 2023
d3390a0
Update src/ko/proposal-object-getownpropertydescriptors.md
chanuuuuu Mar 22, 2023
1343dec
ko : proposal-object-getownpropertydescriptors 번역 수정
chanuuuuu Mar 22, 2023
12733da
summary : proposal-object-getownpropertydescriptors-appendix 추가 수정 (미완)
chanuuuuu Mar 22, 2023
b8c7115
Merge branch '11st-corp:main' into main
chanuuuuu Mar 27, 2023
a28eab1
Merge branch '11st-corp:main' into main
chanuuuuu Mar 28, 2023
f4f684a
ko : proposal-promise-finally 번역 완료
chanuuuuu Mar 28, 2023
8f86516
Merge branch '11st-corp:main' into main
chanuuuuu Mar 30, 2023
6c5b958
Merge branch 'main' into main
chanuuuuu Apr 4, 2023
7522605
Merge remote-tracking branch 'origin'
chanuuuuu Apr 4, 2023
f1440dd
Merge branch 'main' into main
chanuuuuu May 8, 2023
f6cbbe5
Merge branch '11st-corp:main' into main
chanuuuuu May 10, 2023
bf40b57
ko : proposal-string-matchAll 번역
chanuuuuu May 10, 2023
e7689fa
summary : proposal-string-matchAll 번역
chanuuuuu May 10, 2023
9e1cbf6
summary : proposal-string-matchAll 정리 및 수정
chanuuuuu May 10, 2023
6c35e55
Update src/ko/proposal-string-matchAll.md
chanuuuuu Jun 8, 2023
c7862ce
Merge branch 'main' into matchAll
chanuuuuu Jun 14, 2023
59a6f12
fix : @hochan222 피드백 반영
chanuuuuu Jun 14, 2023
12ce2d4
Merge branch 'main' into matchAll
chanuuuuu Jun 14, 2023
80c88e2
Merge branch 'main' into matchAll
chanuuuuu Jun 21, 2023
226edbf
Merge branch 'main' into matchAll
chanuuuuu Jun 27, 2023
44fff90
Merge branch 'main' into matchAll
chanuuuuu Jul 7, 2023
54630e5
Merge branch 'main' into matchAll
chanuuuuu Sep 8, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions src/ko/proposal-string-matchAll.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# String.prototype.matchAll
`String.prototype.matchAll`에 대한 제안서와 명세서입니다.

## 폴리필/Shim
[npm의 string.prototype.matchall](https://www.npmjs.com/package/string.prototype.matchall) 이나, [github](https://github.com/ljharb/String.prototype.matchAll) 을 확인하세요.

## 명세서
[markdown](spec.md) 형식이나 [HTML](https://tc39.github.io/proposal-string-matchall/) 형식으로 명세서를 보실 수 있습니다.
chanuuuuu marked this conversation as resolved.
Show resolved Hide resolved

## 이론적 근거
내가 특정 문자열과 여러개의 캡쳐링 그룹들을 가진 `sticky` / `global` 접근자 속성의 정규식을 가지고 있다면, 모든 매치된 결과를 반복하고 있을 때가 많습니다. 현재, 저는 아래의 방식으로 구현합니다.
chanuuuuu marked this conversation as resolved.
Show resolved Hide resolved

```js
var regex = /t(e)(st(\d?))/g;
var string = 'test1test2';

string.match(regex); // ['test1', 'test2']의 결과가 반환됩니다. 캡쳐링 그룹은 어떻게 접근해야할까요?

var matches = [];
var lastIndexes = {};
var match;
lastIndexes[regex.lastIndex] = true;
while (match = regex.exec(string)) {
lastIndexes[regex.lastIndex] = true;
matches.push(match);
// 예제: `index`과 `input`속성을 가진 배열 ['test1', 'e', 'st1', '1']
}
matches; /* 우리가 원하는 것을 가지고 있지만, 반복문을 사용하였고,
* 정규식이 가진 `lastIndex` 속성을 변경시켰습니다. */
lastIndexes; /* 동일한 { 0: true }의 결과를 원하였으나,
* lastIndex의 변경된 값들을 가지고 있습니다. */

var matches = [];
string.replace(regex, function () {
var match = Array.prototype.slice.call(arguments, 0, -2);
match.input = arguments[arguments.length - 1];
match.index = arguments[arguments.length - 2];
matches.push(match);
// 예제: `index`과 `input`속성을 가진 배열 ['test1', 'e', 'st1', '1']
});
matches; /* 우리가 원하는 것을 가지고 있지만, `replace`를 남용하였고,
* `lastIndex` 속성을 변경시켰을 뿐 만 아니라,
* `match`의 기본 구조를 요구합니다. */
```

첫번째 예제는 캡쳐링 그룹들을 제공하지 않기 때문에, 방법이 아닙니다. 나머지 두개의 예제는 모두 `lastIndex`를 명시적으로 변경하고 있습니다. - 이 것은 모든 매치된 결과에 대한 정확한 정보들을 얻는 지저분한 방법 중에 하나이기 때문에, ES6/ES2015의 내장된 `RegExp`에게 (이론적인 부분 이상으로) 매우 큰 문제입니다.
chanuuuuu marked this conversation as resolved.
Show resolved Hide resolved

그러므로, `String#matchAll`은 모든 캡처링 그룹에 대한 접근법 뿐 만 아니라, 문제가 되는 정규식 객체의 명시적인 변경을 하지않도록 하는 조건을 만족합니다.
chanuuuuu marked this conversation as resolved.
Show resolved Hide resolved

## 반복자 대 배열
많은 상황의 경우, 매치된 결과들의 배열을 원합니다. - 하지만, 명백하게도 모든 상황은 아닙니다. 특히 많은 수의 캡처링 그룹이나 긴 문자열의 크기는 매치된 결과를 배열로 반환하는데에 성능저하를 야기할 수 있습니다. 이를 위해 반복자를 반환함으로서, 사용자가 원하는 경우에만 `Array.from`이나 연산자를 통해서 배열로 변환하는 작업을 진행합니다.
chanuuuuu marked this conversation as resolved.
Show resolved Hide resolved

## 이전의 논의들
- http://blog.stevenlevithan.com/archives/fixing-javascript-regexp
- https://esdiscuss.org/topic/letting-regexp-method-return-something-iterable
- http://stackoverflow.com/questions/844001/javascript-regular-expressions-and-sub-matches
- http://stackoverflow.com/questions/432493/how-do-you-access-the-matched-groups-in-a-javascript-regular-expression
- http://stackoverflow.com/questions/19913667/javascript-regex-global-match-groups
- http://stackoverflow.com/questions/844001/javascript-regular-expressions-and-sub-matches
- http://blog.getify.com/to-capture-or-not/#manually-splitting-string

## 네이밍
`matchAll`이라는 이름은 `match`와 대응되며, 단순히 하나의 매치된 결과값이 아닌 *모든* 매치된 결과값을 반환한다는 의미를 담기 위해서 선택되었습니다. 이 것은 정규 표현식이 문자열 내에 모든 매치된 결과를 지시하는 `global` 플래그와 함께 사용될 것이라는 의미를 담고 있습니다. 해당 이름의 대체안으로 반복자를 반환하는 복수명사의 선례인 `keys`/`values`/`entries`를 따르는 `matches`가 제안되었습니다.
chanuuuuu marked this conversation as resolved.
Show resolved Hide resolved
하지만 `includes`는 boolean 값을 반환합니다. 이름이 명확하게 명사나 동사가 아닐 경우, "복수 명사"는 명백하게 해당 선례를 따라한다고 볼 수 없습니다.
chanuuuuu marked this conversation as resolved.
Show resolved Hide resolved

위원회의 피드백으로부터 수정된 사항 : ruby는 이 메서드에 대해 `scan`이라는 단어를 이름으로 사용합니다. 하지만 한 위원은 Javascript에 새로운 단어를 추가하는 것을 반대하였습니다. `matchEach`가 제안되었으나 일부의 인원이 `forEach`와 이름이 유사함에도, API가 약간은 다르기에 반대하였습니다. `matchAll`이라는 이름을 모두가 찬성하였습니다.
chanuuuuu marked this conversation as resolved.
Show resolved Hide resolved

2017년 9월 TC39 회의에서, "all"의 의미가 "all overlapping matches"인지 "all non-overlapping matches"인지에 대한 질문이 제기되었습니다. - "overlapping"의 의미는 "문자열 내에 각각의 문자로부터 시작하여 매치된 모든 것"이며, "non-overlapping"의 의미는 "문자열의 시작에서부터 매치된 모든 것"입니다. 우리는 메서드의 이름을 변경할 것인지, 두 의미를 모두 만족하는 방법을 추가할 것인가에 대해 간략하게 고려하였으나, 해당 문제는 철회되었습니다.
chanuuuuu marked this conversation as resolved.
Show resolved Hide resolved
101 changes: 101 additions & 0 deletions src/summary/proposal-string-matchAll.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# string.prototype.matchAll을 위한 주변지식

## 캡처링 그룹(Capturing Group)
**정규표현식을 통해 매치된 결과**에 대해서 세부적인 값을 파악하기 위해서 사용되는 그룹

- `()`을 통해 표현하며, `(?<name>{regex})`의 문법으로 네이밍을 할 수 있습니다.
- `matchAll`을 사용하면, name이 `groups`의 key 값으로 사용되며, 캡처링 그룹에 매칭된 값이 대응됩니다.
- `matchAll`의 반환값은 배열임에도 속성을 가지고 있는 형태라 `Array`라고 볼 수 없습니다. `RegExpStringIterator`라는 형태입니다.

```javascript
let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/g;
const result = '2015-01-02,2015-01-03'.matchAll(re);
for (value of result) console.log(value);

/*
['2015-01-02', '2015', '01', '02', index: 0, input: '2015-01-02,2015-01-03', groups: {…}]
0 : "2015-01-02"
1 : "2015"
2 : "01"
3 : "02"
groups : {year: '2015', month: '01', day: '02'}
index : 0
input : "2015-01-02,2015-01-03"
length : 4
[[Prototype]] : Array(0)
*/

/*
['2015-01-03', '2015', '01', '03', index: 11, input: '2015-01-02,2015-01-03', groups: {…}]
0 : "2015-01-03"
1 : "2015"
2 : "01"
3 : "03"
groups : {year: '2015', month: '01', day: '03'}
index : 11
input : "2015-01-02,2015-01-03"
length : 4
[[Prototype]] : Array(0)
*/
```


[네이밍된 캡처링 그룹](https://github.com/tc39/proposal-regexp-named-groups)


## sticky 접근자 속성

정규식 객체의 `sticy` 접근자 속성은 해당 정규식의 `y` 플래그 여부를 의미합니다. 이 때, `y` 플래그는 정규식의 매칭을 `lastIndex` 속성부터 시작한다는 것을 의미합니다.

sticky 정규식과 global 정규식은 아래의 방식으로 매칭이 수행됩니다.
- 정규식 객체가 가진 속성인 `lastIndex`에서부터 매칭을 시작합니다.
- 정규식과 매칭되는 경우, `lastIndex`는 매칭의 결과의 끝 인덱스가 됩니다.
- `lastIndex`가 현재 문자열의 길이를 벗어나면, `lastIndex`는 0으로 초기화됩니다.

[RegExp.prototype.sticky](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/sticky)

`matchAll`의 경우, sticky 정규식과 global 정규식의 방식으로 문자열 전체를 탐색하기 때문에 수행되는 방식을 이해해야합니다.

## 예제 분석

```js
var regex = /t(e)(st(\d?))/g;
var string = 'test1test2';

string.match(regex); // ['test1', 'test2']의 결과가 반환됩니다. 캡쳐링 그룹은 어떻게 접근해야할까요?

var matches = [];
var lastIndexes = {};
var match;
lastIndexes[regex.lastIndex] = true;

while (match = regex.exec(string)) {
lastIndexes[regex.lastIndex] = true;
matches.push(match);
// 예제: `index`과 `input`속성을 가진 배열 ['test1', 'e', 'st1', '1']
}
matches; /* 우리가 원하는 것을 가지고 있지만, 반복문을 사용하였고,
* 정규식이 가진 `lastIndex` 속성을 변경시켰습니다. */
lastIndexes; /* 동일한 { 0: true }의 결과를 원하였으나,
* lastIndex의 변경된 값들을 가지고 있습니다. */


/*
[Array(4), Array(4)]
[
['test1', 'e', 'st1', '1', index: 0, input: 'test1test2', groups: undefined, length: 4],
['test2', 'e', 'st2', '2', index: 5, input: 'test1test2', groups: undefined, length: 4]
]
*/

/*
{0: true, 5: true, 10: true}
*/
```

- `exec`를 사용하면 매칭된 캡처링 그룹에 대해 접근할 수 있습니다. 하지만 반복하기 위해서는 `while`을 사용해야합니다. `matchAll` 메서드를 사용하면 이러한 문법적 제약을 벗어날 수 있고, `for ...of`, `배열 전개연산`, `Array.from()`을 통해서 쉽게 반복자를 생성할 수 있습니다.
- 정규식 객체의 `lastIndex` 속성의 값이 계속해서 변합니다. 해당 속성을 기준으로 반복이 수행되기 때문입니다.

### 요약

#### `lastIndex` 속성의 수정없이 캡처링 그룹의 결과에 대한 반복작업을 위해 `matchAll`을 사용하자.