From 3900c8899b2111dc8ecced8365ddc591ff1ded26 Mon Sep 17 00:00:00 2001 From: 1101707 Date: Wed, 15 Mar 2023 01:21:54 +0900 Subject: [PATCH 01/25] =?UTF-8?q?ko=20:=20proposal-object-getownpropertyde?= =?UTF-8?q?scriptors=20=EB=B2=88=EC=97=AD=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...oposal-object-getownpropertydescriptors.md | 147 ++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 src/ko/proposal-object-getownpropertydescriptors.md diff --git a/src/ko/proposal-object-getownpropertydescriptors.md b/src/ko/proposal-object-getownpropertydescriptors.md new file mode 100644 index 0000000..7ddf976 --- /dev/null +++ b/src/ko/proposal-object-getownpropertydescriptors.md @@ -0,0 +1,147 @@ +# Object.getOwnPropertyDescriptors Proposal + +> [폴리필](https://www.npmjs.com/package/object.getownpropertydescriptors)은 해당 링크에 있습니다. + +## 제안한 사람 + +stage 0 단계에서 **[Rick Waldron](https://github.com/rwaldron)** 이 제안하였으나, 현재 공식적으로 제안한사람은 **[Jordan Harband](https://github.com/ljharb)** 입니다. + +## 적용 상태 + +해당 제안은 현재 [the TC39 process](https://github.com/tc39/ecma262/)의 stage 4 단계에 있습니다. + +해당 제안은 `Reflect.getOwnPropertyDescriptors`과 동일하나, 다른 버전과의 일관성을 위하여 `Object`의 public 정적 메서드로 구현되어있습니다. + +## 제안 동기 + +ECMAScript 에는 두 객체 간의 복사를 구현한 단일 메서드가 존재하지 않습니다. 복잡한 어플리케이션에 함수적 프로그래밍과 불변 객체의 필요성이 대두된 시점에서 모든 프레임워크, 라이브러리가 객체와 프로토 타입들 간의 복사를 자신만의 방식으로 각각 구현하고 있습니다. + +`Object.assign`으로 구현하게 되는 경우, 많은 혼란과 의도하지 않은 동작들이 발생하게 됩니다. 이는 복사가 단순히 **얕은 복사**이기 때문입니다. +(특히 복잡한 객체나 클래스의 프로토타입의 경우, descriptors나, 접근자를 삭제하는 방식이 아닌, 속성과 symbols에 직접 접근하는 복사 방식은 문제가 될 수 있다.) + +열거형의 여부를 떠나서 모든 descriptor를 확인하는 작업은 객체가 기본적으로 비열거형 메서드와 접근자를 가지고 있기 때문에 클래스와 클래스의 프로토타입의 구성을 구현하는데 중요합니다. + +또한 decorator의 경우, 다른 클래스와 믹스인의 descriptor들을 통해 확인할 수 있고 `Object.defineProperties`를 통해 쉽게 할당이 가능하다. 필요하지 않은 descriptor를 필터링하는 것은 반복적이지 않고 간단하다. + +마지막으로, 무엇보다도 두 객체간의 얕은 복사는 `Object.assign`와 거의 차이가 존재하지 않는다. + +## FAQs + +### `Reflect.getOwnPropertyDescriptors`이 꼭 있어야하나? + +이 제안의 목적이 여러 형태의 보일러플레이트를 단순화하고, 여러개의 방법들을 일치시키기 위함이므로 현재 `Reflect.getOwnPropertyDescriptors`의 또 다른 버전으로 생각할 수 있다. + +업데이트 : 위원회는 `Reflect`가 `Proxy`의 트랩을 미러링을 위한 것이므로 옵션이 아니라는 것을 사전에 결정하였습니다. + + +## 제안 로직 + +`Object.getOwnPropertyDescriptor`의 또 다른 버전으로서, 해당 제안은 제네릭 객체이 가진 모든 `descriptor`를 한번의 작업으로 탐색하는 것을 말한다. + +해당 제안의 **polyfill**은 아래와 같다. + +```javascript +if (!Object.hasOwnProperty('getOwnPropertyDescriptors')) { + Object.defineProperty( + Object, + 'getOwnPropertyDescriptors', + { + configurable: true, + writable: true, + value: function getOwnPropertyDescriptors(object) { + return Reflect.ownKeys(object).reduce((descriptors, key) => { + return Object.defineProperty( + descriptors, + key, + { + configurable: true, + enumerable: true, + writable: true, + value: Object.getOwnPropertyDescriptor(object, key) + } + ); + }, {}); + } + } + ); +} +``` + +## 설명하기 위한 예제 + +위의 폴리필은 ES5 또는 부분적인 ES2015를 지원하는 엔진에서 동작하는 보일러플레이트를 개선한 것이다. + +`Object.getOwnPropertyDescriptors`을 통해서 두개의 객체간 얕은 복사와 클로닝이 가능하다. 예제를 보자. + +```javascript +const shallowClone = (object) => Object.create( + Object.getPrototypeOf(object), + Object.getOwnPropertyDescriptors(object) +); + +const shallowMerge = (target, source) => Object.defineProperties( + target, + Object.getOwnPropertyDescriptors(source) +); +``` + +mixin를 통한 객체 또한 이 제안을 통해 개선이 가능하다. + +```javascript +let mix = (object) => ({ + with: (...mixins) => mixins.reduce( + (c, mixin) => Object.create( + c, Object.getOwnPropertyDescriptors(mixin) + ), object) +}); + +// multiple mixins example +let a = {a: 'a'}; +let b = {b: 'b'}; +let c = {c: 'c'}; +let d = mix(c).with(a, b); +``` + +만약 side effect를 피하고 setter/getter를 복사하며 구분 요소로 열거 가능한 속성을 사용하고자 `[[Set]]`/`[[Get]]` 대신 `[[DefineOwnProperty]]`/`[[GetOwnProperty]]`를 사용하는 방식을 `Object.assign`을 사용하여 구현하는 것을 생각해보자. + +제안 이전에 메서드는 아래와 같이 구현될 것이다. + +```javascript +function completeAssign(target, ...sources) { + sources.forEach(source => { + // grab keys descriptors + let descriptors = Object.keys(source).reduce((descriptors, key) => { + descriptors[key] = Object.getOwnPropertyDescriptor(source, key); + return descriptors; + }, {}); + // by default, Object.assign copies enumerable Symbols too + // so grab and filter Symbols as well + Object.getOwnPropertySymbols(source).forEach(sym => { + let descriptor = Object.getOwnPropertyDescriptor(source, sym); + if (descriptor.enumerable) { + descriptors[sym] = descriptor; + } + }); + Object.defineProperties(target, descriptors); + }); + return target; +} +``` + +그러나 `Object.getOwnPropertyDescriptors`를 사용하게 되면, 위의 보일러 플레이트가 아래와 같이 구현 가능하게 된다. + +```javascript +var completeAssign = (target, ...sources) => + sources.reduce((target, source) => { + let descriptors = Object.getOwnPropertyDescriptors(source); + Reflect.ownKeys(descriptors).forEach(key => { + if (!descriptors[key].enumerable) { + delete descriptors[key]; + } + }); + return Object.defineProperties(target, descriptors); + }, target); +``` + + + From 2ccb500059b54c3165b5d7b5b19755021d4171c3 Mon Sep 17 00:00:00 2001 From: chanuuuuu Date: Thu, 16 Mar 2023 01:39:50 +0900 Subject: [PATCH 02/25] =?UTF-8?q?summary=20:=20proposal-object-getownprope?= =?UTF-8?q?rtydescriptors=20=EC=9A=94=EC=95=BD=201=EC=B0=A8=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...oposal-object-getownpropertydescriptors.md | 156 ++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 src/summary/proposal-object-getownpropertydescriptors.md diff --git a/src/summary/proposal-object-getownpropertydescriptors.md b/src/summary/proposal-object-getownpropertydescriptors.md new file mode 100644 index 0000000..bd4f48a --- /dev/null +++ b/src/summary/proposal-object-getownpropertydescriptors.md @@ -0,0 +1,156 @@ +#PR + +## typo: fix mixin solution + +기존의 mixin에 대한 예제를 아래와 같이 사용하는 것이 더 나은 방법이라고 생각한다. + +```javascript +/** mixin solution */ +const mix = obj => ({ + with : (...mixins) => mixins.reduce((c, mixin) => shallowMerge(c, mixin), obj), +}); +``` + +### 기존의 제안 +```javascript +let mix = (object) => ({ + with: (...mixins) => mixins.reduce( + (c, mixin) => Object.create( + c, Object.getOwnPropertyDescriptors(mixin) + ), object) +}); +``` + +### PR의 제안 +```javascript +let mix = (object) => ({ + with: (...mixins) => mixins.reduce( + (c, mixin) => Object.defineProperties( + c, Object.getOwnPropertyDescriptors(mixin) + ), object) +}); +``` + +기존의 `Object.create()`대신 `Object.defineProperties()`를 사용한 것이다. 일단 두가지의 차이부터 알아야한다. + +### `Object.create()` + +> `Object.create()` 메서드는 지정된 프로토타입 객체 및 속성(property)을 갖는 새 객체를 만든다. + +```javascript +Object.create(proto[, propertiesObject]) +``` + +#### `proto` + +- 생성되는 객체가 가질 프로토타입 객체 +- `new` 연산자를 사용하여 생성하면, 기본적으로는 생성자 함수의 prototype 속성의 Object를 바인딩한다. + +#### `propertiesObject` + +- 프로토타입이 아닌 생성되는 객체가 가질 속성을 가진 객체 +- 속성명 : 속성 설명자를 각각 key, value로 가진 객체를 전달한다. +- `Object.defineProperties()`의 두번째 인수에 해당한다. 즉, 해당 함수를 두번째 인수로 넣어 수행하여 객체 내부에 속성을 정의한다. + +#### 고전적인 상속방법 + +```javascript +// Shape - 상위클래스 +function Shape() { + this.x = 0; + this.y = 0; +} + +// Rectangle - 하위클래스 +function Rectangle() { + Shape.call(this); // super 생성자 호출. +} + +// 하위클래스는 상위클래스를 확장 +Rectangle.prototype = Object.create(Shape.prototype); +Rectangle.prototype.constructor = Rectangle; +``` + +- super 생성자 호출 후, `Shape`의 프로토타입 객체를 `Rectangle`의 인스턴스도 사용하기 위해서 `prototype` 속성을 바인딩해야한다. +- `Rectangle.prototype = Shape.prototype`으로 할당을 하게 되면, `Rectangle`의 프로토타입 객체를 변경하게 되면 `Shape`의 인스턴스에게도 영향을 준다. +- 이를 위해 `Shape` 프로토타입 객체를 프로토타입으로 가지는 빈 객체를 생성(`Object.create()`), 해당 객체를 `Rectangle`의 `prototype` 속성으로 바인딩한다. +- `Object.create()`로 생성되는 객체는 `constructor`가 존재하지 않으므로 바인딩 해준다. + + +#### `propertiesObject` 인자 사용하기 + +```javascript +var o = Object.create(Object.prototype, { + foo: { writable: true, configurable: true, value: 'hello' }, + bar: { + configurable: false, + get: function() { return 10; }, + set: function(value) { console.log('Setting `o.bar` to', value); } + } +}); + +``` +- `Object.create()`에서 두번째 인자를 사용하였다. 생성되는 객체는 `foo`, `bar` 두개의 속성을 가진다. +- `foo`는 정규 '값 속성'으로, 기본으로 writable, enumerable 또는 configurable 속성은 `false` 이기 때문에 `{ value : 'hello'}`으로 선언할 수 있다. +- `bar`는 '접근자(accessor, getter-및-setter) 속성'으로 선언되어있다. + +'값 속성' 또는 '접근자 속성'과 같이 속성을 선언할 때 사용되는 객체를 **'속성 설명자'**라고 한다. + + +### `Object.defineProperties()` + +> 인자로 전달된 객체에 속성의 정의하거나 수정하여 객체를 반환한다. + +```javascript +Object.defineProperties(obj, props); +``` + +#### obj +- 속성을 정의하거나 수정할 객체 + +#### props +- `Object.create()`의 두번째 인자인 `propertiesObject`와 동일하다. +- 즉, 속성명과 해당 속성의 설명자(`descriptor`)를 말한다. 로직은 위의 `propertiesObject`과 차이가 없다. + + +### `Object.create(c, Object.getOwnPropertyDescriptors(mixin))`와 `Object.defineProperties(c, Object.getOwnPropertyDescriptors(mixin))`의 차이 + +- `Object.create()`는 `c`객체를 `prototype` 객체로 사용하는 빈 객체를 선언, 해당 객체에 속성의 설명자를 통해 속성을 할당한다. +- `Object.defineProperties()`는 `c`객체에 직접적으로 속성의 설명자를 통해 속성을 할당한다. + +즉, `prototype chain`상 빈 객체를 'bridge'를 두어 속성을 선언할 것인가, 객체에 직접 속성을 선언할 것인가의 차이로 볼 수 있다. 실제로 PR의 제안이 더 나은 방법인가에 대해서는 잘 모르겠다. 이를 결정하기 위해서 `mixin`에 대한 지식이 필요할 것으로 보인다. + + + +## Summary + +어쨌든 이를 통해 `Object.getOwnPropertyDescriptors()`의 의미는 해당 객체가 가지는 속성 설명자를 반환하기 위해 사용된다는 것을 알게 되었다. 아래에 제시하는 개념들에 대한 공부가 더 필요할 것으로 보인다. + +--- + +### mixin + +```javascript +let mix = (object) => ({ + with: (...mixins) => mixins.reduce( + (c, mixin) => Object.create( + c, Object.getOwnPropertyDescriptors(mixin) + ), object) +}); +``` + +### `Reflect` + +`Reflect`를 선언하는 코드는 아래와 같다. + +```javascript + global.Reflect = { + defineProperty: Object.defineProperty, + getOwnPropertyDescriptor: Object.getOwnPropertyDescriptor, + ownKeys: function ownKeys(genericObject) { + let gOPS = Object.getOwnPropertySymbols || function () { return []; }; + return Object.getOwnPropertyNames(genericObject) + .concat(gOPS(genericObject)); + } + }; +``` From 85c49b7669931fe2065d44dd50e336eed7de82cd Mon Sep 17 00:00:00 2001 From: chanuuuuu Date: Sat, 18 Mar 2023 19:19:12 +0900 Subject: [PATCH 03/25] =?UTF-8?q?summary=20:=20proposal-object-getownprope?= =?UTF-8?q?rtydescriptors=20proto=20=EA=B4=80=EB=A0=A8=20=EB=82=B4?= =?UTF-8?q?=EC=9A=A9=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...oposal-object-getownpropertydescriptors.md | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/summary/proposal-object-getownpropertydescriptors.md b/src/summary/proposal-object-getownpropertydescriptors.md index bd4f48a..69fc058 100644 --- a/src/summary/proposal-object-getownpropertydescriptors.md +++ b/src/summary/proposal-object-getownpropertydescriptors.md @@ -44,7 +44,28 @@ Object.create(proto[, propertiesObject]) #### `proto` - 생성되는 객체가 가질 프로토타입 객체 -- `new` 연산자를 사용하여 생성하면, 기본적으로는 생성자 함수의 prototype 속성의 Object를 바인딩한다. +- `new` 연산자를 사용하여 생성하면, 기본적으로는 생성자 함수의 `prototype` 속성의 Object를 바인딩한다. + +Prototype Object는 `__proto__` 속성을 가지는데 아래는 해당 속성의 폴리필이다. + +```javascript +Object.defineProperty( Object.prototype, "__proto__", { + get: function() { + return Object.getPrototypeOf( this ); + }, + set: function(o) { + // setPrototypeOf(..) as of ES6 + Object.setPrototypeOf( this, o ); + return o; + } +} ); +``` + +- Object.prototype 객체에 `__proto__` 속성을 선언한다. +- `defineProperty`는 세번째 인자로 해당 속성의 descriptor를 받는다. +- 실제로 해당 속성은 `Object.getPrototypeOf()`, `Object.setPrototypeOf`를 사용하여 해당 객체(`this`)의 prototype 객체를 반환한다. +- 즉, 기존에 존재하는 prototype 메서드를 쉽게 사용하기 위해서 속성으로 선언한 것으로 볼 수 있다. + #### `propertiesObject` From 8ee2e0f3e8f63c0345c63416a389bd7f0c17a08b Mon Sep 17 00:00:00 2001 From: chanuuuuu Date: Sat, 18 Mar 2023 19:19:25 +0900 Subject: [PATCH 04/25] =?UTF-8?q?summary=20:=20proposal-object-getownprope?= =?UTF-8?q?rtydescriptors=20appendix=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ject-getownpropertydescriptors-appendix.md | 247 ++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 src/summary/proposal-object-getownpropertydescriptors-appendix.md diff --git a/src/summary/proposal-object-getownpropertydescriptors-appendix.md b/src/summary/proposal-object-getownpropertydescriptors-appendix.md new file mode 100644 index 0000000..671243f --- /dev/null +++ b/src/summary/proposal-object-getownpropertydescriptors-appendix.md @@ -0,0 +1,247 @@ +# Object에 속성을 선언하는 방법. + +## 'property accessor', 속성의 접근자 + +> 속성 접근자는 `.`(dot) 또는 `[]`(square bracket)을 사용하여 객체에 접근하는 방법이다. + +- 이 접근자를 통해 역으로, 객체의 속성을 정의할 수 있다. +- 속성 접근자는 `string`, `symbol`만 사용할 수 있다. 이는 값만으로 구별가능해야하기 때문이다. + + +## `Object.defineProperty()`와 'descriptor', 속성의 설명자 + +JS는 객체에 속성을 선언하는 메서드를 `Object.defineProperty()`로 따로 만들어두었다. 이는 'descriptor'를 통해 속성을 정의하기 위함이다. + +```javascript +Object.defineProperty(obj, prop, descriptor) +``` + +### prop + +- 속성의 접근자의 역할을 할 key 값 (`string` or `symbol`) + + +### descriptor (설명자) + +기본적으로 할당을 통해 속성을 선언/수정하게 되면, 해당 속성에 대해 열거, 변경, 삭제가 가능하다. 하지만 해당 메서드를 사용하여 속성을 선언하게 되면 값 뿐 만 아니라 속성의 특성까지 정의할 수 있다. 기본적으로 메서드를 사용하여 선언된 속성은 열거, 수정, 삭제가 불가능하다. +추가적으로 해당 메서드는 할당을 통해 속성을 선언할 때 사용하는 `[[set]]`을 사용하는 것이 아니라, `[[DefineOwnProperty]]`를 사용한다. 즉 내부 로직이 다르기 때문에 속성의 세부적인 특성까지 선언할 수 있는 것이다. + +> 참고로, 객체에 내에 선언되는 속성을 `own property`라고 한다. 이는 객체의 속성이라도 프로토타입 체인에 의해 선언되는 속성이 존재하기 때문에 이를 구별하기 위해 `property`가 아닌 `own property`를 사용하는 것으로 보인다. + +> `enumerable`(열거가능)의 의미는 `for in`, `Object.keys`를 통해 속성을 열거하는 작업을 수행할 때 접근가능한 속성을 의미한다. + +이 때, descriptor는 `value descriptor`, `accessor descriptor` 두 가지를 통해 표현된다. 둘 모두 속성을 설명하는 특성을 객체의 형태로 표현하고 있는데, 두 개의 차이는 다음과 같다. + +1. `value descriptor` - 특정한 값을 가진 속성을 선언하는 방식, 객체 내의 `value` key로 **속성의 값**을 정의한다. +2. `acccessor descriptor` - `getter` `setter`를 선언하는 방식, `get`, `set` key로 **속성을 접근하는 방식**을 정의한다. + +### descriptor가 속성 + +속성(`own property`)을 선언하기 위해 사용하는 descriptor가 가진 속성(`descriptor property`)을 말한다. + +1. `configurable` - 속성의 descriptor의 변경여부. `false`을 경우, descriptor가 변경되지 않으며 해당 속성이 삭제되지 않는다. 하지만 `writable`이 `true`일 경우, 속성의 값은 변경될 수 있으며, `writable` 또한 `false`로 변경이 가능하다. +2. `enumerable` - 객체의 속성을 열거할 때, 속성 접근 가능여부 +3. `writable`- 객체의 값의 변경 가능여부 +4. `get` - 속성에 대한 접근 로직, `this` - 객체 +5. `set` - 속성의 값 할당 로직, `this` - 객체, parameter는 전달되는 값 + +어려운 점은, descriptor 또한 객체이므로 prototype link가 존재하여 prototype chain 상에서 접근가능한 속성(`descriptor property`) 또한 고려될 수 있다는 것이다. 이를 통해 descriptor의 prototype link 상의 prototype object를 변경하는 방식으로 여러 객체의 속성의 descriptor를 변경할 수 있게 된다. + +> **prototype chain의 객체가 가진 descritor 또한 객체의 속성에 영향을 준다.** + +```javascript +function MyClass() {} + +MyClass.prototype.x = 1; +Object.defineProperty(MyClass.prototype, "y", { + writable: false, + value: 1, +}); + +const a = new MyClass(); +a.x = 2; +console.log(a.x); // 2 +console.log(MyClass.prototype.x); // 1 +a.y = 2; // Ignored, throws in strict mode +console.log(a.y); // 1 +console.log(MyClass.prototype.y); // 1 +``` + +- `MyClass`의 prototype object에 `y` 속성을 `descriptor`를 사용하여 선언하였다. +- prototype object가 가진 속성이지만 `MyClass`의 모든 인스턴스에 해당 속성을 상속할 뿐 만 아니라, descriptor의 영향을 받는다. 이로 인해 모든 인스턴스는 `y` 속성에 대한 재선언 및 수정/삭제가 불가능하다. + +그렇다면, descriptor 또한 하나의 객체이므로 descriptor의 prototype chain 상의 속성이 객체의 속성`own property`에 영향을 줄 것인가에 대해서 생각해보자. +descriptor를 인자로 전달할 때, 접근 가능한 속성을 참조하여 snapshot으로 전달한다. 그러므로 descriptor의 prototype chain 상의 값을 변경하게 되더라도 `own property`에 전혀 영향을 주지 않는다. + +```javascript +var po = { writable: true }; + +var o = Object.create(po); +o.value = 3; + +var j = {}; +Object.defineProperty(j, 'y', o); +console.log(j.y); // 3 + +j.y = 4; +console.log(j.y); // 4 + +po.writable = false; +j.y = 5; +console.log(j.y); // 5 +``` + +- `o`는 `j`객체의 `y`속성 descriptor의 역할을 수행한다. `o`의 prototype link가 참조하는 prototype object인 `po`의 `writable`을 변경하여 속성값의 변경을 막고자 하였다. +- 하지만 `y` 속성의 수정이 이루어지는 것으로 보아, descriptor의 prototype object가 변경되더라도 영향을 주지 않는다. 즉, `defineProperty`호출 시점의 객체의 값을 사용한다. (deep copy의 방식) + +#### 결론적으로, descriptor의 prototype chain은 속성(`own property`)에 영향을 주지 않는다. 이는 호출시점에 descriptor의 값을 사용하기 때문이다. + +#### 객체의 prototype link로 참조하고 있는 prototype object에 descriptor로 선언된 속성은 속성(`own property`)에 영향을 준다. + +---- + + + +# Object를 복사하는 방법# Object에 속성을 선언하는 방법. + +## 'property accessor', 속성의 접근자 + +> 속성 접근자는 `.`(dot) 또는 `[]`(square bracket)을 사용하여 객체에 접근하는 방법이다. + +- 이 접근자를 통해 역으로, 객체의 속성을 정의할 수 있다. +- 속성 접근자는 `string`, `symbol`만 사용할 수 있다. 이는 값만으로 구별가능해야하기 때문이다. + + +## `Object.defineProperty()`와 'descriptor', 속성의 설명자 + +JS는 객체에 속성을 선언하는 메서드를 `Object.defineProperty()`로 따로 만들어두었다. 이는 'descriptor'를 통해 속성을 정의하기 위함이다. + +```javascript +Object.defineProperty(obj, prop, descriptor) +``` + +### prop + +- 속성의 접근자의 역할을 할 key 값 (`string` or `symbol`) + + +### descriptor (설명자) + +기본적으로 할당을 통해 속성을 선언/수정하게 되면, 해당 속성에 대해 열거, 변경, 삭제가 가능하다. 하지만 해당 메서드를 사용하여 속성을 선언하게 되면 값 뿐 만 아니라 속성의 특성까지 정의할 수 있다. 기본적으로 메서드를 사용하여 선언된 속성은 열거, 수정, 삭제가 불가능하다. +추가적으로 해당 메서드는 할당을 통해 속성을 선언할 때 사용하는 `[[set]]`을 사용하는 것이 아니라, `[[DefineOwnProperty]]`를 사용한다. 즉 내부 로직이 다르기 때문에 속성의 세부적인 특성까지 선언할 수 있는 것이다. + +> 참고로, 객체에 내에 선언되는 속성을 `own property`라고 한다. 이는 객체의 속성이라도 프로토타입 체인에 의해 선언되는 속성이 존재하기 때문에 이를 구별하기 위해 `property`가 아닌 `own property`를 사용하는 것으로 보인다. + +> `enumerable`(열거가능)의 의미는 `for in`, `Object.keys`를 통해 속성을 열거하는 작업을 수행할 때 접근가능한 속성을 의미한다. + +이 때, descriptor는 `value descriptor`, `accessor descriptor` 두 가지를 통해 표현된다. 둘 모두 속성을 설명하는 특성을 객체의 형태로 표현하고 있는데, 두 개의 차이는 다음과 같다. + +1. `value descriptor` - 특정한 값을 가진 속성을 선언하는 방식, 객체 내의 `value` key로 **속성의 값**을 정의한다. +2. `acccessor descriptor` - `getter` `setter`를 선언하는 방식, `get`, `set` key로 **속성을 접근하는 방식**을 정의한다. + +### descriptor가 속성 + +속성(`own property`)을 선언하기 위해 사용하는 descriptor가 가진 속성(`descriptor property`)을 말한다. + +1. `configurable` - 속성의 descriptor의 변경여부. `false`을 경우, descriptor가 변경되지 않으며 해당 속성이 삭제되지 않는다. 하지만 `writable`이 `true`일 경우, 속성의 값은 변경될 수 있으며, `writable` 또한 `false`로 변경이 가능하다. +2. `enumerable` - 객체의 속성을 열거할 때, 속성 접근 가능여부 +3. `writable`- 객체의 값의 변경 가능여부 +4. `get` - 속성에 대한 접근 로직, `this` - 객체 +5. `set` - 속성의 값 할당 로직, `this` - 객체, parameter는 전달되는 값 + +어려운 점은, descriptor 또한 객체이므로 prototype link가 존재하여 prototype chain 상에서 접근가능한 속성(`descriptor property`) 또한 고려될 수 있다는 것이다. 이를 통해 descriptor의 prototype link 상의 prototype object를 변경하는 방식으로 여러 객체의 속성의 descriptor를 변경할 수 있게 된다. + +> **prototype chain의 객체가 가진 descritor 또한 객체의 속성에 영향을 준다.** + +```javascript +function MyClass() {} + +MyClass.prototype.x = 1; +Object.defineProperty(MyClass.prototype, "y", { + writable: false, + value: 1, +}); + +const a = new MyClass(); +a.x = 2; +console.log(a.x); // 2 +console.log(MyClass.prototype.x); // 1 +a.y = 2; // Ignored, throws in strict mode +console.log(a.y); // 1 +console.log(MyClass.prototype.y); // 1 +``` + +- `MyClass`의 prototype object에 `y` 속성을 `descriptor`를 사용하여 선언하였다. +- prototype object가 가진 속성이지만 `MyClass`의 모든 인스턴스에 해당 속성을 상속할 뿐 만 아니라, descriptor의 영향을 받는다. 이로 인해 모든 인스턴스는 `y` 속성에 대한 재선언 및 수정/삭제가 불가능하다. + +그렇다면, descriptor 또한 하나의 객체이므로 descriptor의 prototype chain 상의 속성이 객체의 속성`own property`에 영향을 줄 것인가에 대해서 생각해보자. +descriptor를 인자로 전달할 때, 접근 가능한 속성을 참조하여 snapshot으로 전달한다. 그러므로 descriptor의 prototype chain 상의 값을 변경하게 되더라도 `own property`에 전혀 영향을 주지 않는다. + +```javascript +var po = { writable: true }; + +var o = Object.create(po); +o.value = 3; + +var j = {}; +Object.defineProperty(j, 'y', o); +console.log(j.y); // 3 + +j.y = 4; +console.log(j.y); // 4 + +po.writable = false; +j.y = 5; +console.log(j.y); // 5 +``` + +- `o`는 `j`객체의 `y`속성 descriptor의 역할을 수행한다. `o`의 prototype link가 참조하는 prototype object인 `po`의 `writable`을 변경하여 속성값의 변경을 막고자 하였다. +- 하지만 `y` 속성의 수정이 이루어지는 것으로 보아, descriptor의 prototype object가 변경되더라도 영향을 주지 않는다. 즉, `defineProperty`호출 시점의 객체의 값을 사용한다. (deep copy의 방식) + +#### 결론적으로, descriptor의 prototype chain은 속성(`own property`)에 영향을 주지 않는다. 이는 호출시점에 descriptor의 값을 사용하기 때문이다. + +#### 객체의 prototype link로 참조하고 있는 prototype object에 descriptor로 선언된 속성은 속성(`own property`)에 영향을 준다. + +[defineProperty MDN 공식 페이지](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty) + +---- + +# Object를 복사하는 방법 + +## 얕은 복사(shallow copy)과 깊은 복사(deep copy) + +얕은 복사와 깊은 복사의 차이는, **원본의 변경이 사본에 영향을 주는가**의 차이이다. +1. shallow copy - 객체가 참조하는 값을 복사한다. JS는 기본적으로 객체의 속성이 해당 값을 참조하는 것이 아니라, 값이 저장된 주솟값을 참조하고 있기 때문에 해당 복사 방법을 사용한다. +2. deep copy - 객체가 참조하는 값의 주소값까지 복사한다. + +추가적으로, +1. call by reference - 전달되는 값을 주소값으로 사용하여, 해당 주소값이 참조하고 있는 값을 사용한다. +2. call by value - 전달되는 값을 그대로 값으로 사용한다. + +## `Object.assign()` + +```javascript +Object.assign(target, ...sources) +``` + + +`target`에 `sources`의 열거가능(`enumerable`)하고, 객체가 가진 속성(`own property`)을 할당한다. 이 때, 이 복사는 shallow copy이다. + + + + + + + + + +## 얕은 복사(shallow copy)과 깊은 복사(deep copy) + + + + + +[defineProperty MDN 공식 페이지](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty) + + + From 8ab1f701816156e39ea6c8e3a2530f859de13008 Mon Sep 17 00:00:00 2001 From: chanuuuuu Date: Sat, 18 Mar 2023 19:20:39 +0900 Subject: [PATCH 05/25] =?UTF-8?q?summary=20:=20proposal-object-getownprope?= =?UTF-8?q?rtydescriptors=20appendix=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...l-object-getownpropertydescriptors-appendix.md | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/summary/proposal-object-getownpropertydescriptors-appendix.md b/src/summary/proposal-object-getownpropertydescriptors-appendix.md index 671243f..21427b2 100644 --- a/src/summary/proposal-object-getownpropertydescriptors-appendix.md +++ b/src/summary/proposal-object-getownpropertydescriptors-appendix.md @@ -228,20 +228,7 @@ Object.assign(target, ...sources) `target`에 `sources`의 열거가능(`enumerable`)하고, 객체가 가진 속성(`own property`)을 할당한다. 이 때, 이 복사는 shallow copy이다. - - - - - - - -## 얕은 복사(shallow copy)과 깊은 복사(deep copy) - - - - - -[defineProperty MDN 공식 페이지](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty) +[Object.assign MDN 공식 페이지](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) From 8ab40258b42744af3281ba93b4466508ed9d2cdf Mon Sep 17 00:00:00 2001 From: chanuuuuu Date: Mon, 20 Mar 2023 21:05:31 +0900 Subject: [PATCH 06/25] =?UTF-8?q?summary=20:=20proposal-object-getownprope?= =?UTF-8?q?rtydescriptors=20appendix=20Object.assign=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ject-getownpropertydescriptors-appendix.md | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/summary/proposal-object-getownpropertydescriptors-appendix.md b/src/summary/proposal-object-getownpropertydescriptors-appendix.md index 21427b2..727fd2c 100644 --- a/src/summary/proposal-object-getownpropertydescriptors-appendix.md +++ b/src/summary/proposal-object-getownpropertydescriptors-appendix.md @@ -225,7 +225,27 @@ Object.assign(target, ...sources) ``` -`target`에 `sources`의 열거가능(`enumerable`)하고, 객체가 가진 속성(`own property`)을 할당한다. 이 때, 이 복사는 shallow copy이다. +`target`에 `sources`의 열거가능(`enumerable`)하고, 객체가 가진 속성(`own property`)을 할당한다. 이 때, 이 복사는 shallow copy이다. +이 때, 중요한 것은 `sources`의 속성 중, `enumerable`한 속성만을 복사한다는 것이다. + +- 속성을 복사할 때, `[[Get]]`을 호출하여 속성의 값을 평가하여 `[[Set]]`을 호출하여 `target`에 속성을 할당한다. +- prototype object에 속성을 할당하는 경우 주로 `Object.defineProperty()`를 사용한다. 이 때 속성을 할당하는 로직이 `[[Set]]`, `[[Get]]`이 아닌 `[[DefineOwnProperty]]`이기 때문에 prototype object에 속성을 할당힐 때는 사용하지 않는다. +- 이 때, 로직이 다른 것도 있지만 `getter`가 선언되는 방식이 아니라 `getter`를 수행하여 나오는 값 자체를 할당하기 때문이다. + +```javascript +const obj = { + foo: 1, + get bar() { + return 2; + }, +}; + +let copy = Object.assign({}, obj); +console.log(copy); +// { foo: 1, bar: 2 } +``` + +기본적으로 `Object.assign`은 얕은 복사이다. 즉, 값만을 복사하여 할당하기 때문에 복사되는 속성이 참조형 타입일 경우 `sources`의 변경이 `target`에 영향을 줄 수 있다. [Object.assign MDN 공식 페이지](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) From 2a38a088b610c892a3926b05520b80b884366524 Mon Sep 17 00:00:00 2001 From: chanuuuuu Date: Mon, 20 Mar 2023 22:33:13 +0900 Subject: [PATCH 07/25] ko : proposal-dotAll-flag-for-reglar-expressions --- src/ko/dotAll-flag-for-regular-expressions.md | 156 ++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 src/ko/dotAll-flag-for-regular-expressions.md diff --git a/src/ko/dotAll-flag-for-regular-expressions.md b/src/ko/dotAll-flag-for-regular-expressions.md new file mode 100644 index 0000000..4c73606 --- /dev/null +++ b/src/ko/dotAll-flag-for-regular-expressions.md @@ -0,0 +1,156 @@ +# 정규식의 `s`(dotAll) flag + + + +## 상태 + +--- + +이 제안은 [the TC39 process](https://tc39.es/process-document/) 의 4단계에 있습니다. + +
+ +## 제안 배경 + +--- + +JS의 정규식에서 마침표(`.`)는 문자의 종류와는 상관없이 단일 문자와 매칭됩니다. *ECMAScript*에서는 이 마침표(`.`)에 두가지 예외사항이 존재합니다. + +1. 마침표(`.`)는 아스트랄 문자와 매칭되지 않습니다. 이 예외사항을 해결하기 위해서는 `u` flag를 사용해야합니다. +2. 마침표(`.`)는 [line terminator characters](https://tc39.es/ecma262/#prod-LineTerminator) 와 매칭되지 않습니다. + +*ECMAScript*에서는 아래의 문자를 `line terminator characters`로 정의하고 있습니다. +- U+000A LINE FEED (LF) (`\n`) +- U+000D CARRIAGE RETURN (CR) (`\r`) +- U+2028 LINE SEPARATOR +- U+2029 PARAGRAPH SEPARATOR + +게다가 사용자 정의에 따라 더 많은 문자들이 `line terminator characters`으로 정의될 수 있습니다. 아래는 그 예시입니다. +- U+000B VERTICAL TAB (`\v`) +- U+000C FORM FEED (`\f`) +- U+0085 NEXT LINE + +두가지 예외사항로 인해 마침표(`.`)는 정규식에 있어서 문제를 발생시키게 됩니다. + +1. 구현 상, `line terminator characters`을 모두 포함하지 않기 때문에 사용자에 따라 마침표(`.`)의 매칭 여부가 달라질 수 있습니다. +2. 일반적으로 마침표(`.`)는 모든 단일 문자에 대하여 매칭된다는 의미로 사용되지만 실제로는 그렇지 않습니다. + +이러한 문제를 해결하기 위해 제안된 `s`(dotAll) flag에 대해 설명하고자 합니다. + +기존의 정규식에서는 `line terminator characters`를 포함한 모든 단일 문자를 대체하기 위해서 마침표(`.`)를 사용하지만 제대로 동작하지 않습니다. + +```javascript +/foo.bar/.test('foo\nbar'); +// → false +``` + +그래서 위의 코드 대신, `[\s\S]`나 `[^]`의 특수한 방식으로 구현해야했습니다. + +```javascript +/foo[^]bar/.test('foo\nbar'); +// → true +``` + +모든 단일 문자와의 매칭은 매우 흔하기 때문에 다른 언어들의 정규식 엔진은 마침표(`.`)를 `line terminator characters`를 포함한 모든 단일 문자와 매칭되도록 하는 모드를 제공합니다. + +- 정규식 flag로 `DOTALL` 또는 `SINGLELINE`/`s`를 제공하는 엔진들 +1. [JAVA](https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html#DOTALL) 는 `Pattern.DOTALL`을 제공합니다. +2. [C#과 VB](https://learn.microsoft.com/en-us/dotnet/api/system.text.regularexpressions.regexoptions?redirectedfrom=MSDN&view=net-7.0) 는 `RegexOptions.Singleline`을 제공합니다. +3. Python은 `re.DOTALL`과 `re.S`를 모두 제공합니다. + +- 내장 flag 표현식 `(?s)`을 제공하는 엔진들 +1. [JAVA](https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html#DOTALL) +2. [C#과 VB](https://learn.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-options) + +- 정규 표현식 flag `s`를 제공하는 엔진들 +1. [Perl](https://perldoc.perl.org/perlre#*s*) +2. [PHP](https://secure.php.net/manual/en/reference.pcre.pattern.modifiers.php#s) + +보편적으로, 이름을 `s`(`singleline`의 줄임)과 `dotAll`으로 사용하고 있습니다. + +하나의 예외사항으로, Ruby는 [the `m` flag(`Regexp::MULTILINE`)](https://ruby-doc.org/core-2.3.3/Regexp.html#method-i-options) 를 통해 `dotAll` 모드를 제공합니다. 안타깝게도 JS에는 `m` flag가 존재하기 때문에 하위 호환성을 고려하여 이 이름은 사용하지 않았습니다. + +## 제안된 해결책 + +--- + +*ECMAScript*의 정규식에 마침표(`.`)와 `line terminator characters`를 포함한 모든 단일 문자를 매칭하는 `s` flag를 새로 도입하고자 합니다. + +```javascript +/foo.bar/s.test('foo\nbar'); +// → true +``` + +
+ +## 상위 API에서의 활용 + +--- + +```javascript +const re = /foo.bar/s; // Or, `const re = new RegExp('foo.bar', 's');`. +re.test('foo\nbar'); +// → true +re.dotAll +// → true +re.flags +// → 's' +``` + +
+ +### FAQ + + +#### 하위 호환성은 어떤가요? + +새롭게 제안된 `s` flag는 기존과는 다른 로직을 필요로 하기 때문에 기존의 다른 정규식 패턴은 영향을 받지 않습니다. + +
+ + +#### `dotAll` 모드는 `multiline` 모드에 영향을 줄 수 있나요? + +이 질문은 `s` flag가 `m`/`multiline`flag의 반대인 `singleline`모드를 의미하는 것으로 오해하기 때문에 발생하고 있지만 실제로는 그렇지 않습니다. +이 것은 단순히 보편적으로 사용하고 있는 네이밍 방식을 차용하였기 때문에 그렇습니다. +보편적으로 사용되는 이름과는 다른 flag의 이름을 사용하게되면 의미의 혼란을 줄 수 있고, `dotAll`이라는 이름이 조금 더 명확한 의미를 담고 있기 때문입니다. +이 때문에 이 모드를 `singleline` 모드 대신 `dotAll` 모드로 사용하도록 권장하고 있습니다. + +`dotAll`와 `multiline` 두 모드는 독립적으로 동작하기 때문에 함께 사용이 가능합니다. 실제로 `multiline`은 오직 `anchors`에만, `dotAll`는 마침표(`.`)에만 영향을 줍니다. + +만약 `dotAll`와 `multiline` 두 모드를 사용하게 되면, `^`와 `$`를 문자열 내의 `line terminator characters`의 처음과 끝으로 매칭함과 동시에 마침표(`.`)는 `line terminator characters`를 포함한 모든 단일 문자에 매칭됩니다. + + +## 명세 + +--- + + +- [Ecmarkup source](https://github.com/tc39/proposal-regexp-dotall-flag/blob/main/spec.html) +- [HTML version](https://tc39.es/proposal-regexp-dotall-flag/) + +
+ +## 구현 + +--- + +- [V8](https://bugs.chromium.org/p/v8/issues/detail?id=6172), shipping in Chrome 62 +- [JavaScriptCore](https://bugs.webkit.org/show_bug.cgi?id=172634), shipping in [Safari Technology Preview 39a](https://developer.apple.com/safari/technology-preview/release-notes/) +- [XS](https://github.com/Moddable-OpenSource/moddable/blob/public/xs/sources/xsre.c), shipping in Moddable as of [the January 17, 2018 update](http://blog.moddable.tech/blog/january-17-2017-big-update-to-moddable-sdk/) +- [regexpu (transpiler)](https://github.com/mathiasbynens/regexpu) with the`{ dotAllFlag: true }`option enabled + - [online demo](https://mothereff.in/regexpu#input=const+regex+%3D+/foo.bar/s%3B%0Aconsole.log%28%0A++regex.test%28%27foo%5Cnbar%27%29%0A%29%3B%0A//+%E2%86%92+true&dotAllFlag=1) + - [Babel plugin](https://github.com/mathiasbynens/babel-plugin-transform-dotall-regex) +- [Compat-transpiler of RegExp Tree](https://github.com/dmitrysoshnikov/regexp-tree#using-compat-transpiler-api) + - [Babel plugin](https://github.com/dmitrysoshnikov/babel-plugin-transform-modern-regexp) + + + + + + + + + + + From 193181ce63c62357e7fbd2c00ea849a157fc1bab Mon Sep 17 00:00:00 2001 From: chanuuuuu Date: Mon, 20 Mar 2023 22:39:51 +0900 Subject: [PATCH 08/25] =?UTF-8?q?delete=20:=20proposal-dotAll-flag-for-reg?= =?UTF-8?q?lar-expressions=20branch=20=EB=B6=84=EB=A6=AC=EB=A5=BC=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ko/dotAll-flag-for-regular-expressions.md | 156 ------------------ 1 file changed, 156 deletions(-) delete mode 100644 src/ko/dotAll-flag-for-regular-expressions.md diff --git a/src/ko/dotAll-flag-for-regular-expressions.md b/src/ko/dotAll-flag-for-regular-expressions.md deleted file mode 100644 index 4c73606..0000000 --- a/src/ko/dotAll-flag-for-regular-expressions.md +++ /dev/null @@ -1,156 +0,0 @@ -# 정규식의 `s`(dotAll) flag - - - -## 상태 - ---- - -이 제안은 [the TC39 process](https://tc39.es/process-document/) 의 4단계에 있습니다. - -
- -## 제안 배경 - ---- - -JS의 정규식에서 마침표(`.`)는 문자의 종류와는 상관없이 단일 문자와 매칭됩니다. *ECMAScript*에서는 이 마침표(`.`)에 두가지 예외사항이 존재합니다. - -1. 마침표(`.`)는 아스트랄 문자와 매칭되지 않습니다. 이 예외사항을 해결하기 위해서는 `u` flag를 사용해야합니다. -2. 마침표(`.`)는 [line terminator characters](https://tc39.es/ecma262/#prod-LineTerminator) 와 매칭되지 않습니다. - -*ECMAScript*에서는 아래의 문자를 `line terminator characters`로 정의하고 있습니다. -- U+000A LINE FEED (LF) (`\n`) -- U+000D CARRIAGE RETURN (CR) (`\r`) -- U+2028 LINE SEPARATOR -- U+2029 PARAGRAPH SEPARATOR - -게다가 사용자 정의에 따라 더 많은 문자들이 `line terminator characters`으로 정의될 수 있습니다. 아래는 그 예시입니다. -- U+000B VERTICAL TAB (`\v`) -- U+000C FORM FEED (`\f`) -- U+0085 NEXT LINE - -두가지 예외사항로 인해 마침표(`.`)는 정규식에 있어서 문제를 발생시키게 됩니다. - -1. 구현 상, `line terminator characters`을 모두 포함하지 않기 때문에 사용자에 따라 마침표(`.`)의 매칭 여부가 달라질 수 있습니다. -2. 일반적으로 마침표(`.`)는 모든 단일 문자에 대하여 매칭된다는 의미로 사용되지만 실제로는 그렇지 않습니다. - -이러한 문제를 해결하기 위해 제안된 `s`(dotAll) flag에 대해 설명하고자 합니다. - -기존의 정규식에서는 `line terminator characters`를 포함한 모든 단일 문자를 대체하기 위해서 마침표(`.`)를 사용하지만 제대로 동작하지 않습니다. - -```javascript -/foo.bar/.test('foo\nbar'); -// → false -``` - -그래서 위의 코드 대신, `[\s\S]`나 `[^]`의 특수한 방식으로 구현해야했습니다. - -```javascript -/foo[^]bar/.test('foo\nbar'); -// → true -``` - -모든 단일 문자와의 매칭은 매우 흔하기 때문에 다른 언어들의 정규식 엔진은 마침표(`.`)를 `line terminator characters`를 포함한 모든 단일 문자와 매칭되도록 하는 모드를 제공합니다. - -- 정규식 flag로 `DOTALL` 또는 `SINGLELINE`/`s`를 제공하는 엔진들 -1. [JAVA](https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html#DOTALL) 는 `Pattern.DOTALL`을 제공합니다. -2. [C#과 VB](https://learn.microsoft.com/en-us/dotnet/api/system.text.regularexpressions.regexoptions?redirectedfrom=MSDN&view=net-7.0) 는 `RegexOptions.Singleline`을 제공합니다. -3. Python은 `re.DOTALL`과 `re.S`를 모두 제공합니다. - -- 내장 flag 표현식 `(?s)`을 제공하는 엔진들 -1. [JAVA](https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html#DOTALL) -2. [C#과 VB](https://learn.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-options) - -- 정규 표현식 flag `s`를 제공하는 엔진들 -1. [Perl](https://perldoc.perl.org/perlre#*s*) -2. [PHP](https://secure.php.net/manual/en/reference.pcre.pattern.modifiers.php#s) - -보편적으로, 이름을 `s`(`singleline`의 줄임)과 `dotAll`으로 사용하고 있습니다. - -하나의 예외사항으로, Ruby는 [the `m` flag(`Regexp::MULTILINE`)](https://ruby-doc.org/core-2.3.3/Regexp.html#method-i-options) 를 통해 `dotAll` 모드를 제공합니다. 안타깝게도 JS에는 `m` flag가 존재하기 때문에 하위 호환성을 고려하여 이 이름은 사용하지 않았습니다. - -## 제안된 해결책 - ---- - -*ECMAScript*의 정규식에 마침표(`.`)와 `line terminator characters`를 포함한 모든 단일 문자를 매칭하는 `s` flag를 새로 도입하고자 합니다. - -```javascript -/foo.bar/s.test('foo\nbar'); -// → true -``` - -
- -## 상위 API에서의 활용 - ---- - -```javascript -const re = /foo.bar/s; // Or, `const re = new RegExp('foo.bar', 's');`. -re.test('foo\nbar'); -// → true -re.dotAll -// → true -re.flags -// → 's' -``` - -
- -### FAQ - - -#### 하위 호환성은 어떤가요? - -새롭게 제안된 `s` flag는 기존과는 다른 로직을 필요로 하기 때문에 기존의 다른 정규식 패턴은 영향을 받지 않습니다. - -
- - -#### `dotAll` 모드는 `multiline` 모드에 영향을 줄 수 있나요? - -이 질문은 `s` flag가 `m`/`multiline`flag의 반대인 `singleline`모드를 의미하는 것으로 오해하기 때문에 발생하고 있지만 실제로는 그렇지 않습니다. -이 것은 단순히 보편적으로 사용하고 있는 네이밍 방식을 차용하였기 때문에 그렇습니다. -보편적으로 사용되는 이름과는 다른 flag의 이름을 사용하게되면 의미의 혼란을 줄 수 있고, `dotAll`이라는 이름이 조금 더 명확한 의미를 담고 있기 때문입니다. -이 때문에 이 모드를 `singleline` 모드 대신 `dotAll` 모드로 사용하도록 권장하고 있습니다. - -`dotAll`와 `multiline` 두 모드는 독립적으로 동작하기 때문에 함께 사용이 가능합니다. 실제로 `multiline`은 오직 `anchors`에만, `dotAll`는 마침표(`.`)에만 영향을 줍니다. - -만약 `dotAll`와 `multiline` 두 모드를 사용하게 되면, `^`와 `$`를 문자열 내의 `line terminator characters`의 처음과 끝으로 매칭함과 동시에 마침표(`.`)는 `line terminator characters`를 포함한 모든 단일 문자에 매칭됩니다. - - -## 명세 - ---- - - -- [Ecmarkup source](https://github.com/tc39/proposal-regexp-dotall-flag/blob/main/spec.html) -- [HTML version](https://tc39.es/proposal-regexp-dotall-flag/) - -
- -## 구현 - ---- - -- [V8](https://bugs.chromium.org/p/v8/issues/detail?id=6172), shipping in Chrome 62 -- [JavaScriptCore](https://bugs.webkit.org/show_bug.cgi?id=172634), shipping in [Safari Technology Preview 39a](https://developer.apple.com/safari/technology-preview/release-notes/) -- [XS](https://github.com/Moddable-OpenSource/moddable/blob/public/xs/sources/xsre.c), shipping in Moddable as of [the January 17, 2018 update](http://blog.moddable.tech/blog/january-17-2017-big-update-to-moddable-sdk/) -- [regexpu (transpiler)](https://github.com/mathiasbynens/regexpu) with the`{ dotAllFlag: true }`option enabled - - [online demo](https://mothereff.in/regexpu#input=const+regex+%3D+/foo.bar/s%3B%0Aconsole.log%28%0A++regex.test%28%27foo%5Cnbar%27%29%0A%29%3B%0A//+%E2%86%92+true&dotAllFlag=1) - - [Babel plugin](https://github.com/mathiasbynens/babel-plugin-transform-dotall-regex) -- [Compat-transpiler of RegExp Tree](https://github.com/dmitrysoshnikov/regexp-tree#using-compat-transpiler-api) - - [Babel plugin](https://github.com/dmitrysoshnikov/babel-plugin-transform-modern-regexp) - - - - - - - - - - - From 1104c2467d5d1a3166363c5d6a9aed12e2a84ca3 Mon Sep 17 00:00:00 2001 From: Chanwoo Park <42171155+chanuuuuu@users.noreply.github.com> Date: Thu, 23 Mar 2023 00:11:48 +0900 Subject: [PATCH 09/25] Update src/ko/proposal-object-getownpropertydescriptors.md Co-authored-by: HyukJoo Kwon <76726411+huckjoo@users.noreply.github.com> --- src/ko/proposal-object-getownpropertydescriptors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ko/proposal-object-getownpropertydescriptors.md b/src/ko/proposal-object-getownpropertydescriptors.md index 7ddf976..bfbc4e1 100644 --- a/src/ko/proposal-object-getownpropertydescriptors.md +++ b/src/ko/proposal-object-getownpropertydescriptors.md @@ -8,7 +8,7 @@ stage 0 단계에서 **[Rick Waldron](https://github.com/rwaldron)** 이 제안 ## 적용 상태 -해당 제안은 현재 [the TC39 process](https://github.com/tc39/ecma262/)의 stage 4 단계에 있습니다. +해당 제안은 현재 [the TC39 process](https://github.com/tc39/ecma262/)의 [4 단계](https://github.com/tc39/proposals/blob/main/finished-proposals.md)에 있습니다. 해당 제안은 `Reflect.getOwnPropertyDescriptors`과 동일하나, 다른 버전과의 일관성을 위하여 `Object`의 public 정적 메서드로 구현되어있습니다. From d855099f143dee240423bba2467db49eaafee4d9 Mon Sep 17 00:00:00 2001 From: Chanwoo Park <42171155+chanuuuuu@users.noreply.github.com> Date: Thu, 23 Mar 2023 00:12:06 +0900 Subject: [PATCH 10/25] Update src/ko/proposal-object-getownpropertydescriptors.md Co-authored-by: HyukJoo Kwon <76726411+huckjoo@users.noreply.github.com> --- src/ko/proposal-object-getownpropertydescriptors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ko/proposal-object-getownpropertydescriptors.md b/src/ko/proposal-object-getownpropertydescriptors.md index bfbc4e1..ea1c88c 100644 --- a/src/ko/proposal-object-getownpropertydescriptors.md +++ b/src/ko/proposal-object-getownpropertydescriptors.md @@ -14,7 +14,7 @@ stage 0 단계에서 **[Rick Waldron](https://github.com/rwaldron)** 이 제안 ## 제안 동기 -ECMAScript 에는 두 객체 간의 복사를 구현한 단일 메서드가 존재하지 않습니다. 복잡한 어플리케이션에 함수적 프로그래밍과 불변 객체의 필요성이 대두된 시점에서 모든 프레임워크, 라이브러리가 객체와 프로토 타입들 간의 복사를 자신만의 방식으로 각각 구현하고 있습니다. +ECMAScript에는 두 객체 간의 복사를 구현한 단일 메서드가 존재하지 않습니다. 복잡한 어플리케이션에 함수적 프로그래밍과 불변 객체의 필요성이 대두된 시점에서 모든 프레임워크, 라이브러리가 객체와 프로토타입들 간의 복사를 자신만의 방식으로 각각 구현하고 있습니다. `Object.assign`으로 구현하게 되는 경우, 많은 혼란과 의도하지 않은 동작들이 발생하게 됩니다. 이는 복사가 단순히 **얕은 복사**이기 때문입니다. (특히 복잡한 객체나 클래스의 프로토타입의 경우, descriptors나, 접근자를 삭제하는 방식이 아닌, 속성과 symbols에 직접 접근하는 복사 방식은 문제가 될 수 있다.) From f98c490cadceff6b824b9118a2111c427959e29b Mon Sep 17 00:00:00 2001 From: Chanwoo Park <42171155+chanuuuuu@users.noreply.github.com> Date: Thu, 23 Mar 2023 00:15:32 +0900 Subject: [PATCH 11/25] Update src/ko/proposal-object-getownpropertydescriptors.md Co-authored-by: HyukJoo Kwon <76726411+huckjoo@users.noreply.github.com> --- src/ko/proposal-object-getownpropertydescriptors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ko/proposal-object-getownpropertydescriptors.md b/src/ko/proposal-object-getownpropertydescriptors.md index ea1c88c..3d3b6ba 100644 --- a/src/ko/proposal-object-getownpropertydescriptors.md +++ b/src/ko/proposal-object-getownpropertydescriptors.md @@ -25,7 +25,7 @@ ECMAScript에는 두 객체 간의 복사를 구현한 단일 메서드가 존 마지막으로, 무엇보다도 두 객체간의 얕은 복사는 `Object.assign`와 거의 차이가 존재하지 않는다. -## FAQs +## 자주 묻는 질문들 ### `Reflect.getOwnPropertyDescriptors`이 꼭 있어야하나? From 46c171621e5c4004ab099f84d275eaa13fab73a5 Mon Sep 17 00:00:00 2001 From: Chanwoo Park <42171155+chanuuuuu@users.noreply.github.com> Date: Thu, 23 Mar 2023 00:15:51 +0900 Subject: [PATCH 12/25] Update src/ko/proposal-object-getownpropertydescriptors.md Co-authored-by: HyukJoo Kwon <76726411+huckjoo@users.noreply.github.com> --- src/ko/proposal-object-getownpropertydescriptors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ko/proposal-object-getownpropertydescriptors.md b/src/ko/proposal-object-getownpropertydescriptors.md index 3d3b6ba..59cb728 100644 --- a/src/ko/proposal-object-getownpropertydescriptors.md +++ b/src/ko/proposal-object-getownpropertydescriptors.md @@ -21,7 +21,7 @@ ECMAScript에는 두 객체 간의 복사를 구현한 단일 메서드가 존 열거형의 여부를 떠나서 모든 descriptor를 확인하는 작업은 객체가 기본적으로 비열거형 메서드와 접근자를 가지고 있기 때문에 클래스와 클래스의 프로토타입의 구성을 구현하는데 중요합니다. -또한 decorator의 경우, 다른 클래스와 믹스인의 descriptor들을 통해 확인할 수 있고 `Object.defineProperties`를 통해 쉽게 할당이 가능하다. 필요하지 않은 descriptor를 필터링하는 것은 반복적이지 않고 간단하다. +또한 decorator는 다른 클래스 또는 믹스인에서 descriptor를 한 번에 가져와`Object.defineProperties`를 통해 쉽게 할당이 가능합니다. 필요하지 않은 descriptor를 필터링하는 것도 더 간단할 뿐만 아니라 매번 덜 반복적일 수 있습니다. 마지막으로, 무엇보다도 두 객체간의 얕은 복사는 `Object.assign`와 거의 차이가 존재하지 않는다. From f59b405906391e0a9a3bf90c02623b4e4b5b6d44 Mon Sep 17 00:00:00 2001 From: Chanwoo Park <42171155+chanuuuuu@users.noreply.github.com> Date: Thu, 23 Mar 2023 00:15:59 +0900 Subject: [PATCH 13/25] Update src/ko/proposal-object-getownpropertydescriptors.md Co-authored-by: HyukJoo Kwon <76726411+huckjoo@users.noreply.github.com> --- src/ko/proposal-object-getownpropertydescriptors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ko/proposal-object-getownpropertydescriptors.md b/src/ko/proposal-object-getownpropertydescriptors.md index 59cb728..a5f506f 100644 --- a/src/ko/proposal-object-getownpropertydescriptors.md +++ b/src/ko/proposal-object-getownpropertydescriptors.md @@ -29,7 +29,7 @@ ECMAScript에는 두 객체 간의 복사를 구현한 단일 메서드가 존 ### `Reflect.getOwnPropertyDescriptors`이 꼭 있어야하나? -이 제안의 목적이 여러 형태의 보일러플레이트를 단순화하고, 여러개의 방법들을 일치시키기 위함이므로 현재 `Reflect.getOwnPropertyDescriptors`의 또 다른 버전으로 생각할 수 있다. +이 제안의 목적이 여러 형태의 보일러플레이트를 단순화하고, 여러개의 방법들을 일치시키기 위함이므로 현재 `Reflect.getOwnPropertyDescriptors`의 또 다른 버전으로 생각할 수 있습니다. 업데이트 : 위원회는 `Reflect`가 `Proxy`의 트랩을 미러링을 위한 것이므로 옵션이 아니라는 것을 사전에 결정하였습니다. From f0ba809fff323a6baca41033eb6d01d59db1329b Mon Sep 17 00:00:00 2001 From: Chanwoo Park <42171155+chanuuuuu@users.noreply.github.com> Date: Thu, 23 Mar 2023 00:18:38 +0900 Subject: [PATCH 14/25] Update src/ko/proposal-object-getownpropertydescriptors.md Co-authored-by: HyukJoo Kwon <76726411+huckjoo@users.noreply.github.com> --- src/ko/proposal-object-getownpropertydescriptors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ko/proposal-object-getownpropertydescriptors.md b/src/ko/proposal-object-getownpropertydescriptors.md index a5f506f..7853b97 100644 --- a/src/ko/proposal-object-getownpropertydescriptors.md +++ b/src/ko/proposal-object-getownpropertydescriptors.md @@ -34,7 +34,7 @@ ECMAScript에는 두 객체 간의 복사를 구현한 단일 메서드가 존 업데이트 : 위원회는 `Reflect`가 `Proxy`의 트랩을 미러링을 위한 것이므로 옵션이 아니라는 것을 사전에 결정하였습니다. -## 제안 로직 +## 제안된 해결책 `Object.getOwnPropertyDescriptor`의 또 다른 버전으로서, 해당 제안은 제네릭 객체이 가진 모든 `descriptor`를 한번의 작업으로 탐색하는 것을 말한다. From 94405947bf164ea54cf1bada4af33061360106de Mon Sep 17 00:00:00 2001 From: Chanwoo Park <42171155+chanuuuuu@users.noreply.github.com> Date: Thu, 23 Mar 2023 00:18:44 +0900 Subject: [PATCH 15/25] Update src/ko/proposal-object-getownpropertydescriptors.md Co-authored-by: HyukJoo Kwon <76726411+huckjoo@users.noreply.github.com> --- src/ko/proposal-object-getownpropertydescriptors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ko/proposal-object-getownpropertydescriptors.md b/src/ko/proposal-object-getownpropertydescriptors.md index 7853b97..d50ec5d 100644 --- a/src/ko/proposal-object-getownpropertydescriptors.md +++ b/src/ko/proposal-object-getownpropertydescriptors.md @@ -36,7 +36,7 @@ ECMAScript에는 두 객체 간의 복사를 구현한 단일 메서드가 존 ## 제안된 해결책 -`Object.getOwnPropertyDescriptor`의 또 다른 버전으로서, 해당 제안은 제네릭 객체이 가진 모든 `descriptor`를 한번의 작업으로 탐색하는 것을 말한다. +`Object.getOwnPropertyDescriptor`의 또 다른 버전으로서, 본 제안은 제네릭 객체가 가진 모든 `descriptor`를 한번의 작업으로 탐색하는 것에 관한 것입니다. 해당 제안의 **polyfill**은 아래와 같다. From 65d48600806efc3ff053ca70eeda2ac9e93c81b6 Mon Sep 17 00:00:00 2001 From: Chanwoo Park <42171155+chanuuuuu@users.noreply.github.com> Date: Thu, 23 Mar 2023 00:18:53 +0900 Subject: [PATCH 16/25] Update src/ko/proposal-object-getownpropertydescriptors.md Co-authored-by: HyukJoo Kwon <76726411+huckjoo@users.noreply.github.com> --- src/ko/proposal-object-getownpropertydescriptors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ko/proposal-object-getownpropertydescriptors.md b/src/ko/proposal-object-getownpropertydescriptors.md index d50ec5d..ab012f2 100644 --- a/src/ko/proposal-object-getownpropertydescriptors.md +++ b/src/ko/proposal-object-getownpropertydescriptors.md @@ -38,7 +38,7 @@ ECMAScript에는 두 객체 간의 복사를 구현한 단일 메서드가 존 `Object.getOwnPropertyDescriptor`의 또 다른 버전으로서, 본 제안은 제네릭 객체가 가진 모든 `descriptor`를 한번의 작업으로 탐색하는 것에 관한 것입니다. -해당 제안의 **polyfill**은 아래와 같다. +해당 제안의 **polyfill**은 아래와 같습니다. ```javascript if (!Object.hasOwnProperty('getOwnPropertyDescriptors')) { From d3390a0d8072048e9051286e712df4b9ddf4b4c4 Mon Sep 17 00:00:00 2001 From: Chanwoo Park <42171155+chanuuuuu@users.noreply.github.com> Date: Thu, 23 Mar 2023 00:19:11 +0900 Subject: [PATCH 17/25] Update src/ko/proposal-object-getownpropertydescriptors.md Co-authored-by: HyukJoo Kwon <76726411+huckjoo@users.noreply.github.com> --- src/ko/proposal-object-getownpropertydescriptors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ko/proposal-object-getownpropertydescriptors.md b/src/ko/proposal-object-getownpropertydescriptors.md index ab012f2..07c1963 100644 --- a/src/ko/proposal-object-getownpropertydescriptors.md +++ b/src/ko/proposal-object-getownpropertydescriptors.md @@ -69,7 +69,7 @@ if (!Object.hasOwnProperty('getOwnPropertyDescriptors')) { ## 설명하기 위한 예제 -위의 폴리필은 ES5 또는 부분적인 ES2015를 지원하는 엔진에서 동작하는 보일러플레이트를 개선한 것이다. +위의 폴리필은 ES5 또는 부분적인 ES2015를 지원하는 엔진에서 동작하는 보일러플레이트를 개선하는 ES2015 친화적인 대안을 제시합니다. `Object.getOwnPropertyDescriptors`을 통해서 두개의 객체간 얕은 복사와 클로닝이 가능하다. 예제를 보자. From 1343deca1398f610a51b38b7639f02dc467ef1a9 Mon Sep 17 00:00:00 2001 From: chanuuuuu Date: Thu, 23 Mar 2023 01:23:50 +0900 Subject: [PATCH 18/25] =?UTF-8?q?ko=20:=20proposal-object-getownpropertyde?= =?UTF-8?q?scriptors=20=EB=B2=88=EC=97=AD=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...oposal-object-getownpropertydescriptors.md | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/ko/proposal-object-getownpropertydescriptors.md b/src/ko/proposal-object-getownpropertydescriptors.md index 07c1963..46367d3 100644 --- a/src/ko/proposal-object-getownpropertydescriptors.md +++ b/src/ko/proposal-object-getownpropertydescriptors.md @@ -4,39 +4,37 @@ ## 제안한 사람 -stage 0 단계에서 **[Rick Waldron](https://github.com/rwaldron)** 이 제안하였으나, 현재 공식적으로 제안한사람은 **[Jordan Harband](https://github.com/ljharb)** 입니다. +stage 0 단계에서 **[Rick Waldron](https://github.com/rwaldron)** 이 제안하였으나, 현재 공식적으로 제안한 사람은 **[Jordan Harband](https://github.com/ljharb)** 입니다. ## 적용 상태 -해당 제안은 현재 [the TC39 process](https://github.com/tc39/ecma262/)의 [4 단계](https://github.com/tc39/proposals/blob/main/finished-proposals.md)에 있습니다. +본 제안은 현재 [the TC39 process](https://github.com/tc39/ecma262/)의 [4 단계](https://github.com/tc39/proposals/blob/main/finished-proposals.md)에 있습니다. -해당 제안은 `Reflect.getOwnPropertyDescriptors`과 동일하나, 다른 버전과의 일관성을 위하여 `Object`의 public 정적 메서드로 구현되어있습니다. +본 제안은 `Reflect.getOwnPropertyDescriptors`과 동일하나, 다른 버전과의 호환을 위하여 `Object`의 public 정적 메서드로 구현되어 있습니다. ## 제안 동기 -ECMAScript에는 두 객체 간의 복사를 구현한 단일 메서드가 존재하지 않습니다. 복잡한 어플리케이션에 함수적 프로그래밍과 불변 객체의 필요성이 대두된 시점에서 모든 프레임워크, 라이브러리가 객체와 프로토타입들 간의 복사를 자신만의 방식으로 각각 구현하고 있습니다. +ECMAScript에는 두 객체 간의 복사를 구현한 단일 메서드가 존재하지 않습니다. 어플리케이션이 점점 복잡해짐에 따라 함수적 프로그래밍과 불변 객체가 더더욱 필요하게 되었고, 많은 프레임워크와 라이브러리가 복잡한 객체와 프로토타입 간의 속성을 복사하는 보일러플레이트를 각자의 방식으로 구현하기 시작하였습니다. -`Object.assign`으로 구현하게 되는 경우, 많은 혼란과 의도하지 않은 동작들이 발생하게 됩니다. 이는 복사가 단순히 **얕은 복사**이기 때문입니다. -(특히 복잡한 객체나 클래스의 프로토타입의 경우, descriptors나, 접근자를 삭제하는 방식이 아닌, 속성과 symbols에 직접 접근하는 복사 방식은 문제가 될 수 있다.) +두 객체 간의 복사를 `Object.assign`을 사용하여 구현하게 되는 경우, 많은 혼란과 의도하지 않은 동작들이 발생하게 됩니다. 이는 `Object.assign`을 사용한 복사가 **얕은 복사**이기 때문입니다. (`Object.assign`은 객체의 속성이 가진 descriptor가 아닌 속성의 값 / symbol을 접근하여 복사하기 때문입니다.) 이렇게 속성이 가진 잠재적 accessors를 사용하지 않는 복사는 특히 복잡한 객체나 클래스의 프로토타입을 구성할 때 매우 큰 오류가 발생할 수 있습니다. -열거형의 여부를 떠나서 모든 descriptor를 확인하는 작업은 객체가 기본적으로 비열거형 메서드와 접근자를 가지고 있기 때문에 클래스와 클래스의 프로토타입의 구성을 구현하는데 중요합니다. +객체는 기본적으로 열거불가능한 메서드나 accessors가 존재하기 때문에 객체의 속성이 열거가능 여부과 무관하게 모든 descriptor를 탐색하는 로직은 클래스와 클래스의 프로토타입의 구성을 구현하는데 중요합니다. -또한 decorator는 다른 클래스 또는 믹스인에서 descriptor를 한 번에 가져와`Object.defineProperties`를 통해 쉽게 할당이 가능합니다. 필요하지 않은 descriptor를 필터링하는 것도 더 간단할 뿐만 아니라 매번 덜 반복적일 수 있습니다. +또한 decorator는 다른 클래스 또는 믹스인에서 descriptor를 한 번에 가져올 수 있고, 이 descriptor들을 통해 `Object.defineProperties`로 쉽게 속성을 정의할 수 있습니다. 불필요한 descriptor를 필터링하는 것이 더 간단해지고, 단순해질 것입니다. -마지막으로, 무엇보다도 두 객체간의 얕은 복사는 `Object.assign`와 거의 차이가 존재하지 않는다. +무엇보다도 두 객체간의 얕은 복사는 `Object.assign`로 충분히 구현 가능하기 때문입니다. ## 자주 묻는 질문들 ### `Reflect.getOwnPropertyDescriptors`이 꼭 있어야하나? -이 제안의 목적이 여러 형태의 보일러플레이트를 단순화하고, 여러개의 방법들을 일치시키기 위함이므로 현재 `Reflect.getOwnPropertyDescriptors`의 또 다른 버전으로 생각할 수 있습니다. - -업데이트 : 위원회는 `Reflect`가 `Proxy`의 트랩을 미러링을 위한 것이므로 옵션이 아니라는 것을 사전에 결정하였습니다. +이 제안의 목적은 여러 형태의 보일러플레이트를 단순화하며, 여러가지 구현 방식을 하나로 모으기 위함이기 때문에 단 하나의 방식을 제시하는 것은 아닙니다. 하지만 현재 사용되고 있는 `Reflect.getOwnPropertyDescriptors` 또한 하나의 방식으로 일치시킬 수 있을 것으로 보입니다. +업데이트 : 위원회는 `Reflect`가 `Proxy`의 트랩을 미러링을 위한 것이므로 하나의 '방식'으로 보고 있지 않습니다. ## 제안된 해결책 -`Object.getOwnPropertyDescriptor`의 또 다른 버전으로서, 본 제안은 제네릭 객체가 가진 모든 `descriptor`를 한번의 작업으로 탐색하는 것에 관한 것입니다. +`Object.getOwnPropertyDescriptor`를 사용하여 제네릭 객체가 가진 모든 `descriptor`를 한번의 작업으로 탐색하는 방법을 제안하고자 합니다. 해당 제안의 **polyfill**은 아래와 같습니다. @@ -67,11 +65,11 @@ if (!Object.hasOwnProperty('getOwnPropertyDescriptors')) { } ``` -## 설명하기 위한 예제 +## 설명하기 위한 예시 위의 폴리필은 ES5 또는 부분적인 ES2015를 지원하는 엔진에서 동작하는 보일러플레이트를 개선하는 ES2015 친화적인 대안을 제시합니다. -`Object.getOwnPropertyDescriptors`을 통해서 두개의 객체간 얕은 복사와 클로닝이 가능하다. 예제를 보자. +아래의 예제에서 보이듯, `Object.getOwnPropertyDescriptors`을 통해서 두개의 객체간 얕은 복사와 클로닝이 가능합니다. ```javascript const shallowClone = (object) => Object.create( @@ -85,7 +83,7 @@ const shallowMerge = (target, source) => Object.defineProperties( ); ``` -mixin를 통한 객체 또한 이 제안을 통해 개선이 가능하다. +믹스인을 사용하는 객체 또한 이 제안을 통해 개선이 가능합니다. ```javascript let mix = (object) => ({ @@ -102,9 +100,9 @@ let c = {c: 'c'}; let d = mix(c).with(a, b); ``` -만약 side effect를 피하고 setter/getter를 복사하며 구분 요소로 열거 가능한 속성을 사용하고자 `[[Set]]`/`[[Get]]` 대신 `[[DefineOwnProperty]]`/`[[GetOwnProperty]]`를 사용하는 방식을 `Object.assign`을 사용하여 구현하는 것을 생각해보자. +만약 얕은 복사로 인한 side effect를 피하고 `setter`/`getter`를 복사하거나, 객체간 구별된 열거가능 속성을 사용하기 위해서 `[[Set]]`/`[[Get]]` 대신 `[[DefineOwnProperty]]`/`[[GetOwnProperty]]`를 사용하는 방식을 `Object.assign`을 사용하여 구현하는 것을 생각해보겠습니다. -제안 이전에 메서드는 아래와 같이 구현될 것이다. +제안 이전에 메서드는 아래와 같이 구현됩니다. ```javascript function completeAssign(target, ...sources) { @@ -128,7 +126,7 @@ function completeAssign(target, ...sources) { } ``` -그러나 `Object.getOwnPropertyDescriptors`를 사용하게 되면, 위의 보일러 플레이트가 아래와 같이 구현 가능하게 된다. +그러나 `Object.getOwnPropertyDescriptors`를 사용하게 되면, 위의 보일러 플레이트가 아래와 같이 구현 가능하게 됩니다. ```javascript var completeAssign = (target, ...sources) => From 12733dad47afedea445ae1e15c6088a5b3434ac2 Mon Sep 17 00:00:00 2001 From: chanuuuuu Date: Thu, 23 Mar 2023 01:24:43 +0900 Subject: [PATCH 19/25] =?UTF-8?q?summary=20:=20proposal-object-getownprope?= =?UTF-8?q?rtydescriptors-appendix=20=EC=B6=94=EA=B0=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(=EB=AF=B8=EC=99=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../proposal-object-getownpropertydescriptors-appendix.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/summary/proposal-object-getownpropertydescriptors-appendix.md b/src/summary/proposal-object-getownpropertydescriptors-appendix.md index 727fd2c..cddf55c 100644 --- a/src/summary/proposal-object-getownpropertydescriptors-appendix.md +++ b/src/summary/proposal-object-getownpropertydescriptors-appendix.md @@ -101,8 +101,7 @@ console.log(j.y); // 5 ---- - -# Object를 복사하는 방법# Object에 속성을 선언하는 방법. +# Object에 속성을 선언하는 방법. ## 'property accessor', 속성의 접근자 From f4f684a4fd9aabce06d209f356f35efda4f58e92 Mon Sep 17 00:00:00 2001 From: chanuuuuu Date: Wed, 29 Mar 2023 01:19:56 +0900 Subject: [PATCH 20/25] =?UTF-8?q?ko=20:=20proposal-promise-finally=20?= =?UTF-8?q?=EB=B2=88=EC=97=AD=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ko/proposal-promise-finally.md | 44 ++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/ko/proposal-promise-finally.md diff --git a/src/ko/proposal-promise-finally.md b/src/ko/proposal-promise-finally.md new file mode 100644 index 0000000..2e77113 --- /dev/null +++ b/src/ko/proposal-promise-finally.md @@ -0,0 +1,44 @@ +# Promise.prototype.finally + +`Promise.prototype.finally`의 ECMAScript 제안, 스펙, 참조 문헌을 제시합니다. + +해당 스펙은 [cancelable promise proposal](https://github.com/tc39/proposal-cancelable-promises/blob/e31520fc9a53a8cbeff53b0df413d9e565b27d69/Third%20State.md#promiseprototypefinally-implementation) 에 따라 [@ljharb](https://github.com/ljharb) 에 의해 작성되었습니다. + +폴리필/shim은 [npm](https://www.npmjs.com/package/promise.prototype.finally) 에 있습니다. + +해당 제안은 현재 [process](https://tc39.es/process-document/) 상의 [stage 4](https://github.com/tc39/proposals/blob/main/finished-proposals.md) 에 있습니다. + + +# 이론적 해석 + +많은 promise 라이브러리들은 promise가 완료(`fulfilled` 또는 `rejected`)되었을 때 발생하는 콜백을 등록하는 `finally` 메서드를 가지고 있습니다. 가장 기초적인 예제는 `cleanup` 입니다. 이는 AJAX 요청 중 보여지는 "loading" 스피너를 숨기게 하고 싶다거나, 열려있는 파일을 닫게 한다거나, 어떠한 작업이 진행되었을 때 성공여부를 떠나 로그를 남기고 싶을 때를 말합니다. + +## `then(f, f)`을 사용하면 되지 않을까요? + +`promise.finally(func)`은 `promise.then(func, func)`과 유사하면서도 몇몇 중요한 차이가 존재합니다. + +- 함수를 inline 방식으로 사용하는 경우, 해당 함수를 두번 정의하거나 변수로 정의하지 않고 한번만으로 전달할 수 있습니다. +- `finally` 콜백은 promise의 성공 / 실패여부와 무관하기 때문에 어떠한 인자도 받지 않습니다. 실패 사유나 성공시 전달되는 반환 값이 필요하지 않는 경우에만 사용합니다. +- `Promise.resolve(2).then(() => {}, () => {})`은 promise가 `undefined`를 반환하며 성공하지만, `Promise.resolve(2).finally(() => {})`는 `2`를 반환하며 성공합니다. +- 유사하게 `Promise.reject(3).then(() => {}, () => {})`은 promiserk `undefined`를 반환하며 실패하지만, `Promise.reject(3).finally(() => {})`는 `3`을 반환하며 실패합니다. + + +그러나 `finally` 콜백 내의 `throw`나 rejected된 promise를 반환하는 경우, 실패 사유와 함께 새로운 promise를 `reject`할 것입니다. + +# 네이밍 + +`finally`라는 이름을 고수한 이유는, 직관적이기 때문입니다. `catch`와 마찬가지로 `finally`는 `try`/`catch`/`finally`의 구문 형식을 따르고 있습니다. (물론 `try`는 `Promise.resolve().then`과 유사성이 떨어집니다.) finally 구문은 예외 처리를 한다거나 빠르게 반환하는 것과 같이 "갑작스러운 완료"를 통해 반환 값을 수정할 수 있습니다. 하지만 `Promise#finally`는 내부에서 예외를 던짐(promise를 reject 시킴)으로서 갑작스럽게 완료시키는 것을 제외하고는, 반환값을 수정할 수 없습니다. 이는 promise의 정상 종료와 종료보다 이른 `return undefined` 간의 차이를 구별할 수 없을 뿐 만 아니라 finally 구문의 병렬 처리는 반드시 동시성에 대한 차이가 존재하기 때문입니다. + +저는 순서의 의미를 지니지 않은 단어인 `always`를 대체로 고려하였으나, 저는 통사적 변화에 대한 유사점이 설득력 있다고 생각합니다. + + +# 구현 + +- [Bluebird#finally](http://bluebirdjs.com/docs/api/finally.html) +- [Q#finally](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) +- [when#finally](https://github.com/cujojs/when/blob/master/docs/api.md#promisefinally) +- [jQuery jqXHR#always](https://api.jquery.com/jQuery.ajax/#jqXHR) + +# 스펙 + +스펙은 [markdown format](https://github.com/tc39/proposal-promise-finally/blob/main/spec.md) 으로 볼 수 있거나, [HTML](https://tc39.es/proposal-promise-finally/) 을 통해 확인이 가능합니다. \ No newline at end of file From bf40b57b305a0845ea228b0bde6730e12867353e Mon Sep 17 00:00:00 2001 From: chanuuuuu Date: Thu, 11 May 2023 01:09:32 +0900 Subject: [PATCH 21/25] =?UTF-8?q?ko=20:=20proposal-string-matchAll=20?= =?UTF-8?q?=EB=B2=88=EC=97=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ko/proposal-string-matchAll.md | 68 ++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 src/ko/proposal-string-matchAll.md diff --git a/src/ko/proposal-string-matchAll.md b/src/ko/proposal-string-matchAll.md new file mode 100644 index 0000000..4d19aea --- /dev/null +++ b/src/ko/proposal-string-matchAll.md @@ -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/) 형식으로 명세서를 보실 수 있습니다. + +## 이론적 근거 +내가 특정 문자열과 여러개의 캡쳐링 그룹들을 가진 `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의 변경된 값들을 가지고 있습니다. */ + +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`에게 (이론적인 부분 이상으로) 매우 큰 문제입니다. + +그러므로, `String#matchAll`은 모든 캡처링 그룹에 대한 접근법 뿐 만 아니라, 문제가 되는 정규식 객체의 명시적인 변경을 하지않도록 하는 조건을 만족합니다. + +## 반복자 대 배열 +많은 상황의 경우, 매치된 결과들의 배열을 원합니다. - 하지만, 명백하게도 모든 상황은 아닙니다. 특히 많은 수의 캡처링 그룹이나 긴 문자열의 크기는 매치된 결과를 배열로 반환하는데에 성능저하를 야기할 수 있습니다. 이를 위해 반복자를 반환함으로서, 사용자가 원하는 경우에만 `Array.from`이나 연산자를 통해서 배열로 변환하는 작업을 진행합니다. + +## 이전의 논의들 +- 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`가 제안되었습니다. + 하지만 `includes`는 boolean 값을 반환합니다. 이름이 명확하게 명사나 동사가 아닐 경우, "복수 명사"는 명백하게 해당 선례를 따라한다고 볼 수 없습니다. + +위원회의 피드백으로부터 수정된 사항 : ruby는 이 메서드에 대해 `scan`이라는 단어를 이름으로 사용합니다. 하지만 한 위원은 Javascript에 새로운 단어를 추가하는 것을 반대하였습니다. `matchEach`가 제안되었으나 일부의 인원이 `forEach`와 이름이 유사함에도, API가 약간은 다르기에 반대하였습니다. `matchAll`이라는 이름을 모두가 찬성하였습니다. + +2017년 9월 TC39 회의에서, "all"의 의미가 "all overlapping matches"인지 "all non-overlapping matches"인지에 대한 질문이 제기되었습니다. - "overlapping"의 의미는 "문자열 내에 각각의 문자로부터 시작하여 매치된 모든 것"이며, "non-overlapping"의 의미는 "문자열의 시작에서부터 매치된 모든 것"입니다. 우리는 메서드의 이름을 변경할 것인지, 두 의미를 모두 만족하는 방법을 추가할 것인가에 대해 간략하게 고려하였으나, 해당 문제는 철회되었습니다. \ No newline at end of file From e7689fa9b5848b1a6e69e6c6a4574524f50a599a Mon Sep 17 00:00:00 2001 From: chanuuuuu Date: Thu, 11 May 2023 01:50:06 +0900 Subject: [PATCH 22/25] =?UTF-8?q?summary=20:=20proposal-string-matchAll=20?= =?UTF-8?q?=EB=B2=88=EC=97=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/summary/proposal-string-matchAll.md | 99 +++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 src/summary/proposal-string-matchAll.md diff --git a/src/summary/proposal-string-matchAll.md b/src/summary/proposal-string-matchAll.md new file mode 100644 index 0000000..1ef130f --- /dev/null +++ b/src/summary/proposal-string-matchAll.md @@ -0,0 +1,99 @@ +# string.prototype.matchAll + +## 캡처링 그룹(Capturing Group) +**정규표현식을 통해 매치된 결과**에 대해서 세부적인 값을 파악하기 위해서 사용되는 그룹 + +- `()`을 통해 표현하며, `(?{regex})`의 문법으로 네이밍을 할 수 있습니다. +- `matchAll`을 사용하면, name이 `groups`의 key 값으로 사용되며, 캡처링 그룹에 매칭된 값이 대응됩니다. +- `matchAll`의 반환값은 배열임에도 속성을 가지고 있는 형태라 `Array`라고 볼 수 없습니다. `RegExpStringIterator`라는 형태입니다. + +```javascript +let re = /(?\d{4})-(?\d{2})-(?\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`을 사용하자. \ No newline at end of file From 9e1cbf69825c662724e073f04d68414ffe1f5b19 Mon Sep 17 00:00:00 2001 From: chanuuuuu Date: Thu, 11 May 2023 01:51:41 +0900 Subject: [PATCH 23/25] =?UTF-8?q?summary=20:=20proposal-string-matchAll=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC=20=EB=B0=8F=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/summary/proposal-string-matchAll.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/summary/proposal-string-matchAll.md b/src/summary/proposal-string-matchAll.md index 1ef130f..0505138 100644 --- a/src/summary/proposal-string-matchAll.md +++ b/src/summary/proposal-string-matchAll.md @@ -1,4 +1,4 @@ -# string.prototype.matchAll +# string.prototype.matchAll을 위한 주변지식 ## 캡처링 그룹(Capturing Group) **정규표현식을 통해 매치된 결과**에 대해서 세부적인 값을 파악하기 위해서 사용되는 그룹 @@ -96,4 +96,6 @@ lastIndexes; /* 동일한 { 0: true }의 결과를 원하였으나, - `exec`를 사용하면 매칭된 캡처링 그룹에 대해 접근할 수 있습니다. 하지만 반복하기 위해서는 `while`을 사용해야합니다. `matchAll` 메서드를 사용하면 이러한 문법적 제약을 벗어날 수 있고, `for ...of`, `배열 전개연산`, `Array.from()`을 통해서 쉽게 반복자를 생성할 수 있습니다. - 정규식 객체의 `lastIndex` 속성의 값이 계속해서 변합니다. 해당 속성을 기준으로 반복이 수행되기 때문입니다. -### `lastIndex` 속성의 수정없이 캡처링 그룹의 결과에 대한 반복작업을 위해 `matchAll`을 사용하자. \ No newline at end of file +### 요약 + +#### `lastIndex` 속성의 수정없이 캡처링 그룹의 결과에 대한 반복작업을 위해 `matchAll`을 사용하자. \ No newline at end of file From 6c35e55490825e31b029f400dbbffdf90c079222 Mon Sep 17 00:00:00 2001 From: Chanwoo Park <42171155+chanuuuuu@users.noreply.github.com> Date: Thu, 8 Jun 2023 16:08:31 +0900 Subject: [PATCH 24/25] Update src/ko/proposal-string-matchAll.md Co-authored-by: hochan Lee --- src/ko/proposal-string-matchAll.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ko/proposal-string-matchAll.md b/src/ko/proposal-string-matchAll.md index 4d19aea..0334767 100644 --- a/src/ko/proposal-string-matchAll.md +++ b/src/ko/proposal-string-matchAll.md @@ -5,7 +5,7 @@ [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/) 형식으로 명세서를 보실 수 있습니다. +명세서를 [마크다운 형식](https://github.com/tc39/proposal-string-matchall/blob/main/spec.md) 으로 보거나 [HTML](https://tc39.github.io/proposal-string-matchall/) 로 렌더링할 수 있습니다. ## 이론적 근거 내가 특정 문자열과 여러개의 캡쳐링 그룹들을 가진 `sticky` / `global` 접근자 속성의 정규식을 가지고 있다면, 모든 매치된 결과를 반복하고 있을 때가 많습니다. 현재, 저는 아래의 방식으로 구현합니다. From 59a6f122fca0b472d48072747d169b703ee4eab9 Mon Sep 17 00:00:00 2001 From: chanuuuuu Date: Thu, 15 Jun 2023 00:16:30 +0900 Subject: [PATCH 25/25] =?UTF-8?q?fix=20:=20@hochan222=20=ED=94=BC=EB=93=9C?= =?UTF-8?q?=EB=B0=B1=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ko/proposal-string-matchAll.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/ko/proposal-string-matchAll.md b/src/ko/proposal-string-matchAll.md index 0334767..bd45a9c 100644 --- a/src/ko/proposal-string-matchAll.md +++ b/src/ko/proposal-string-matchAll.md @@ -8,7 +8,7 @@ 명세서를 [마크다운 형식](https://github.com/tc39/proposal-string-matchall/blob/main/spec.md) 으로 보거나 [HTML](https://tc39.github.io/proposal-string-matchall/) 로 렌더링할 수 있습니다. ## 이론적 근거 -내가 특정 문자열과 여러개의 캡쳐링 그룹들을 가진 `sticky` / `global` 접근자 속성의 정규식을 가지고 있다면, 모든 매치된 결과를 반복하고 있을 때가 많습니다. 현재, 저는 아래의 방식으로 구현합니다. +문자열과 여러개의 캡쳐링 그룹을 가진 고정 정규식이나 전역 정규식을 가지고 있다면, 모든 일치된 결과를 반복하고 싶을 때가 많습니다. 현재, 저는 아래의 방법으로 구현합니다. ```js var regex = /t(e)(st(\d?))/g; @@ -43,12 +43,12 @@ matches; /* 우리가 원하는 것을 가지고 있지만, `replace`를 남용 * `match`의 기본 구조를 요구합니다. */ ``` -첫번째 예제는 캡쳐링 그룹들을 제공하지 않기 때문에, 방법이 아닙니다. 나머지 두개의 예제는 모두 `lastIndex`를 명시적으로 변경하고 있습니다. - 이 것은 모든 매치된 결과에 대한 정확한 정보들을 얻는 지저분한 방법 중에 하나이기 때문에, ES6/ES2015의 내장된 `RegExp`에게 (이론적인 부분 이상으로) 매우 큰 문제입니다. +첫번째 예제는 캡쳐링 그룹들을 제공하지 않기 때문에, 올바른 방법이 아닙니다. 후자의 두 예제는 모두 `lastIndex`를 가시적으로 변경하고 있습니다. 이 것은 내장된 `RegExp`에서 (이념적인 문제 이상으로) 큰 문제가 아닙니다. 그러나 ES6/ES2015의 하위 분류 가능한 `RegExp`에서 이것은 모든 일치된 결과에 대한 정확한 정보들을 얻는 지저분한 방법 중에 하나입니다. -그러므로, `String#matchAll`은 모든 캡처링 그룹에 대한 접근법 뿐 만 아니라, 문제가 되는 정규식 객체의 명시적인 변경을 하지않도록 하는 조건을 만족합니다. +그러므로, `String#matchAll`은 모든 캡처링 그룹에 대한 접근을 제공하고 문제가 되는 정규식 객체의 가시적인 변경을 하지 않음으로써 이 사용 사례를 해결할 수 있습니다. ## 반복자 대 배열 -많은 상황의 경우, 매치된 결과들의 배열을 원합니다. - 하지만, 명백하게도 모든 상황은 아닙니다. 특히 많은 수의 캡처링 그룹이나 긴 문자열의 크기는 매치된 결과를 배열로 반환하는데에 성능저하를 야기할 수 있습니다. 이를 위해 반복자를 반환함으로서, 사용자가 원하는 경우에만 `Array.from`이나 연산자를 통해서 배열로 변환하는 작업을 진행합니다. +많은 사용 사례에서 일치된 결과들의 배열을 원할 수 있지만, 명백하게도 모든 사용 사례가 일치하는 것은 아닙니다. 특히 많은 수의 캡처링 그룹이나 큰 문자열은 항상 그들을 배열로 수집하기 위해 성능에 영향을 야기할 수 있습니다. 반복자를 반환함으로서, 호출자가 원하는 경우에만 `Array.from`이나 전개 구문을 통해서 배열로 수집할 수 있지만, 그럴 필요는 없습니다. ## 이전의 논의들 - http://blog.stevenlevithan.com/archives/fixing-javascript-regexp @@ -60,9 +60,8 @@ matches; /* 우리가 원하는 것을 가지고 있지만, `replace`를 남용 - http://blog.getify.com/to-capture-or-not/#manually-splitting-string ## 네이밍 -`matchAll`이라는 이름은 `match`와 대응되며, 단순히 하나의 매치된 결과값이 아닌 *모든* 매치된 결과값을 반환한다는 의미를 담기 위해서 선택되었습니다. 이 것은 정규 표현식이 문자열 내에 모든 매치된 결과를 지시하는 `global` 플래그와 함께 사용될 것이라는 의미를 담고 있습니다. 해당 이름의 대체안으로 반복자를 반환하는 복수명사의 선례인 `keys`/`values`/`entries`를 따르는 `matches`가 제안되었습니다. - 하지만 `includes`는 boolean 값을 반환합니다. 이름이 명확하게 명사나 동사가 아닐 경우, "복수 명사"는 명백하게 해당 선례를 따라한다고 볼 수 없습니다. +`matchAll`이라는 이름은 `match`와 대응되며, 단순히 하나의 일치된 결과 값이 아닌 모든 일치된 결과 값을 반환한다는 의미를 내포하기 위해서 선택 되었습니다. 이것은 문자열에서 모든 일치된 결과를 찾기 위해 제공된 정규식이 전역 플래그와 함께 사용된다는 의미를 내포하고 있습니다. 해당 이름의 대체안으로 반복자를 반환하는 복수명사의 선례인 `keys`/`values`/`entries`를 따르는 `matches`가 제안되었습니다. 하지만 `includes`는 boolean 값을 반환합니다. 단어가 명확한 명사나 동사가 아닐 경우, "복수 명사"는 명백하게 해당 선례를 따라한다고 볼 수 없습니다. -위원회의 피드백으로부터 수정된 사항 : ruby는 이 메서드에 대해 `scan`이라는 단어를 이름으로 사용합니다. 하지만 한 위원은 Javascript에 새로운 단어를 추가하는 것을 반대하였습니다. `matchEach`가 제안되었으나 일부의 인원이 `forEach`와 이름이 유사함에도, API가 약간은 다르기에 반대하였습니다. `matchAll`이라는 이름을 모두가 찬성하였습니다. +위원회의 피드백 갱신사항 : ruby는 이 메서드에 대해 `scan`이라는 단어를 이름으로 사용합니다. 하지만 한 위원은 Javascript에 새로운 단어를 추가하는 것을 반대하였습니다. `matchEach`가 제안되었으나 일부의 인원이 `forEach`와 이름이 유사함에도, API가 약간은 다르기에 반대하였습니다. `matchAll`이라는 이름을 모두가 찬성하였습니다. -2017년 9월 TC39 회의에서, "all"의 의미가 "all overlapping matches"인지 "all non-overlapping matches"인지에 대한 질문이 제기되었습니다. - "overlapping"의 의미는 "문자열 내에 각각의 문자로부터 시작하여 매치된 모든 것"이며, "non-overlapping"의 의미는 "문자열의 시작에서부터 매치된 모든 것"입니다. 우리는 메서드의 이름을 변경할 것인지, 두 의미를 모두 만족하는 방법을 추가할 것인가에 대해 간략하게 고려하였으나, 해당 문제는 철회되었습니다. \ No newline at end of file +2017년 9월 TC39 회의에서, "all"의 의미가 "all overlapping matches"인지 "all non-overlapping matches"인지에 대한 질문이 제기되었습니다. - "overlapping"의 의미는 "문자열 내에 각각의 문자로부터 시작하여 일치된 모든 것"이며, "non-overlapping"의 의미는 "문자열의 시작에서부터 일치된 모든 것"입니다. 우리는 메서드의 이름을 변경할 것인지, 두 의미론을 모두 만족하는 방법을 추가할 것인가에 대해 간략하게 고려하였으나, 이의 제기는 철회되었습니다. \ No newline at end of file