From 3900c8899b2111dc8ecced8365ddc591ff1ded26 Mon Sep 17 00:00:00 2001 From: 1101707 Date: Wed, 15 Mar 2023 01:21:54 +0900 Subject: [PATCH 01/21] =?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/21] =?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/21] =?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/21] =?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/21] =?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/21] =?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/21] 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/21] =?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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] =?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/21] =?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/21] =?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 ba17b1ab95c312f7458cec0f492e0be88074b256 Mon Sep 17 00:00:00 2001 From: chanuuuuu Date: Wed, 14 Jun 2023 00:06:48 +0900 Subject: [PATCH 21/21] =?UTF-8?q?ko=20:=20proposal-object-hasOwn=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-object-hasOwn.md | 188 +++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 src/ko/proposal-object-hasOwn.md diff --git a/src/ko/proposal-object-hasOwn.md b/src/ko/proposal-object-hasOwn.md new file mode 100644 index 0000000..33930ec --- /dev/null +++ b/src/ko/proposal-object-hasOwn.md @@ -0,0 +1,188 @@ + +# 접근 가능한 `Object.prototype.hasOwnProperty()` + +`Object.prototype.hasOwnProperty()`를 더 접근가능 하도록 만드는 `Object.hasOwn()` 메서드에 대한 제안서입니다. + + +## 👋 현재 커뮤니티 피드백 수집중 + +현재 당신의 코드 내의 `Object.hasOwn()`에 사용할 수 있는 폴리필과 codemod에 대한 [구현](#implementations)섹션을 확인하시길 바랍니다. + +만약 `Object.hasOwn()`를 사용하고 있다면, [issue #18](https://github.com/tc39/proposal-accessible-object-hasownproperty/issues/18)에 피드백을 남겨주세요. (긍정적인 피드백이나 부정적인 피드백 모두 권장합니다.) + +## 상태 +해당 제안서는 현재 [4단계](https://github.com/tc39/proposals/blob/master/finished-proposals.md)에 있습니다. + +작성자: + +- [@jamiebuilds](https://github.com/jamiebuilds) (Jamie Kyle, Rome) +- 투사: [@bnb](https://github.com/bnb) (Tierney Cyren, Microsoft) + +슬라이드: + +- [1단계](https://docs.google.com/presentation/d/1FvDwrmzin_qGMzH-Cc8l5bHK91UxkpZJwuugoay5aNQ/edit#slide=id.p) [2021/04](https://github.com/tc39/agendas/blob/master/2021/04.md)에 제작 (2단계 도달) +- [2단계](https://docs.google.com/presentation/d/1r5_Jw-gR8cRNo7SJyWtd6h_fEyVFJr9t3a2FvCBPiLE/edit?usp=sharing) [2021/05](https://github.com/tc39/agendas/blob/master/2021/05.md)에 제작 (3단계 도달) +- [3단계 업데이트](https://docs.google.com/presentation/d/1UbbNOjNB6XpMGo1GGwl0b8lVsNoCPPPLBByPYc7i5IY/edit?usp=sharing) 2021/07에 제작 +- [4단계](https://docs.google.com/presentation/d/177vM52Cd6Dij-ta6vmw4Wi1sCKrzbCKjavSBpbdz9fM/edit?usp=sharing) 2021/08에 제작 (4단계 도달) + +## 제안 동기 + +현재, 아래와 같이 코드를 작성하는 것은 (특히 라이브러리 내에서) 일반적입니다. +```js +let hasOwnProperty = Object.prototype.hasOwnProperty + +if (hasOwnProperty.call(object, "foo")) { + console.log("has property foo") +} +``` + +이 제안서는 위의 코드를 아래와 같이 간략하게 할 수 있습니다. +```js +if (Object.hasOwn(object, "foo")) { + console.log("has property foo") +} +``` + +이러한 편리함을 제공하는 몇 가지 라이브러리입니다. + +- [npm: has][npm-has] +- [npm: lodash.has][npm-lodash-has] +- [See Related](#related) + +이 것은 `Object.prototype`의 메서드를 사용하지 못하거나 재선언될 수 있기 때문에 일반적인 방법입니다. + + +### `Object.create(null)` + +`Object.create(null)` 는 `Object.prototype`를 상속받지 않는 객체를 생성하므로, 이 것이 가진 메서드에 대해 접근이 불가능합니다. + +```js +Object.create(null).hasOwnProperty("foo") +// Uncaught TypeError: Object.create(...).hasOwnProperty is not a function +``` + +### `hasOwnProperty`의 재정의 + +객체에 정의되어 있는 속성을 직접적으로 소유하지 않는 경우, 당신은 내장 함수인 `.hasOwnProperty()`의 호출을 100% 확신할 수 없습니다. + +```js +let object = { + hasOwnProperty() { + throw new Error("gotcha!") + } +} + +object.hasOwnProperty("foo") +// Uncaught Error: gotcha! +``` + +### ESLint의 `no-prototype-builtins` 규칙 + +ESLint는 `hasOwnProperty`와 같은 프로토타입 내장객체의 사용을 금지하는 [내장 규칙][eslint-no-prototype-builtins]을 가집니다. + +> **the ESLint 공식 문서의 `no-prototype-builtins`에 대한 설명** +> +> --- +> +> 해당 규칙에 대한 잘못된 예 +> +> ```js +> /*eslint no-prototype-builtins: "error"*/ +> var hasBarProperty = foo.hasOwnProperty("bar"); +> ... +> ``` +> +> 해당 규칙에 대한 올바른 예 +> +> ```js +> /*eslint no-prototype-builtins: "error"*/ +> var hasBarProperty = Object.prototype.hasOwnProperty.call(foo, "bar"); +> ... +> ``` +> + +### MDN의 `hasOwnProperty()` 에 대한 조언 + +MDN 공식문서는 `Object.prototype.hasOwnProperty`에 대해 프로토타입 체인의 메서드를 직접적으로 사용하지 말라는 [조언][mdn-hasownproperty-advice]을 포함합니다. + +> JavaScript는 hasOwnProperty 속성의 이름을 보호하지 않습니다. 그래서 객체가 이 이름의 속성을 가질 수 있다는 가능성이 존재한다면, 올바른 결과값을 가지기 위해서 외부의 hasOwnProperty를 사용할 필요가 있습니다. .... + +## 제안 + +이 제안은 `hasOwnProperty.call(object, property)`의 호출과 동일한 동작을 하는 `Object.hasOwn(object, property)`메서드를 포함합니다. + +```js +let object = { foo: false } +Object.hasOwn(object, "foo") // true + +let object2 = Object.create({ foo: true }) +Object.hasOwn(object2, "foo") // false + +let object3 = Object.create(null) +Object.hasOwn(object3, "foo") // false +``` + +## 구현사항 + +JavaScript 엔진 내의 `Object.hasOwn`이 사용가능한 기본 구현은 아래에 있습니다. + +- 브라우저: + - [V8](https://chromium-review.googlesource.com/c/v8/v8/+/2922117) ([shipped](https://v8.dev/blog/v8-release-93)) + - [SpiderMonkey](https://hg.mozilla.org/try/rev/94515f78324e83d4fd84f4b0ab764b34aabe6d80) (feature-flagged) + - [JavaScriptCore](https://bugs.webkit.org/show_bug.cgi?id=226291#c2) (in-progress) +- 그외에: + - [SerenityOS: LibJS](https://github.com/SerenityOS/serenity/commit/3ee092cd0cacb999469e50aa5ff220e397df2d79) + - [engine262](https://github.com/engine262/engine262/pull/163) + +`Object.hasOwn()`의 폴리필은 아래에 있습니다. + +- [./polyfill.js](./polyfill.js) +- [npm: object.hasown](https://www.npmjs.com/package/object.hasown) +- [core-js](https://github.com/zloirock/core-js/#accessible-objecthasownproperty) + +유사한 라이브러리들에서부터 `Object.hasOwn()`로 마이그레이션하기 위한 codemod는 아래에 있습니다. + +- [`Object.hasOwn()` codemod](https://gist.github.com/jamiebuilds/f4ff76397d31b69c484240379170af8c) + +`hasOwnProperty` 대신 `hasOwn`로 사용하도록 강제하는 eslint 규칙은 아래에 있습니다. + +- [`unicorn/prefer-object-has-own`](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-object-has-own.md) + +## 질의 응답 + +### 왜 `Object.hasOwnProperty(object, property)`가 아닌가요? + +현재 이미 존재하고 있는 `Object.hasOwnProperty(property)`는 `Object.prototype`로부터 `Object`가 상속받은 것입니다. 그러므로 다른 시그니처를 가진 새로운 메서드를 정의하는 변화가 필요합니다. + +### 왜 이름이 `hasOwn`인가요? + +[이슈 #3](https://github.com/tc39/proposal-accessible-object-hasownproperty/issues/3)를 확인하세요. + +### 객체 대신 딕셔너리의 `Map`을 사용하는 것은 어떻습니까? + +https://v8.dev/features/object-fromentries#objects-vs.-maps의 발췌문입니다. + +> JavaScript는 일반적인 객체보다 조금 더 적합한 데이터 구조로 사용되는 Maps 또한 제공합니다. 그래서 완벽하게 통제가 가능한 코드 내에서는 객체 대신 map을 사용할 수 있습니다. 그러나 개발자로서 항상 확신하지는 못합니다. 가끔 당신이 다루는 데이터는 외부의 API나 라이브러리 함수 들로부터 map이 아닌 객체로 제공됩니다. + +### `Reflect`에 해당 메서드가 제공되나요? + +`Reflect`의 의도는 `Proxy` 트랩에 대한 메서드를 1:1로 포함하는 것입니다. `Proxy`에는 이미 `hasOwnProperty` (`getOwnPropertyDescriptor`)를 트랩하는 메서드가 존재합니다. 그러므로 추가적인 트랩을 추가하는 것은 올바르지 않으며, `Reflect`에 해당 메서드를 포함시키는 것 또한 올바르지 않습니다. + + +## 관련 문서 + +- [npm: `has`][npm-has] +- [npm: `lodash.has`][npm-lodash-has] +- [underscore `_.has`][underscore-has] +- [npm: `just-has`][npm-just-has] +- [ramda: `R.has`][ramda-has] +- [eslint `no-prototype-builtins`][eslint-no-prototype-builtins] +- [MDN `hasOwnProperty()` advice][mdn-hasownproperty-advice] + +[npm-has]: https://www.npmjs.com/package/has +[npm-lodash-has]: https://www.npmjs.com/package/lodash.has +[underscore-has]: https://underscorejs.org/#has +[npm-just-has]: https://www.npmjs.com/package/just-has +[ramda-has]: https://ramdajs.com/docs/#has +[eslint-no-prototype-builtins]: https://eslint.org/docs/rules/no-prototype-builtins +[mdn-hasownproperty-advice]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty#using_hasownproperty_as_a_property_name \ No newline at end of file