You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
-[Object.setPrototypeOf(obj, proto)](mdn:js/Object/setPrototypeOf) -- `obj`의 `[[Prototype]]`이 `proto`가 되도록 설정합니다.
13
13
14
-
앞으론 아래 예시처럼 `__proto__` 대신 이 메서드들을 사용하도록 합시다.
14
+
앞으론 아래 예시처럼 `__proto__` 대신 메서드를 사용하도록 합시다.
15
15
16
16
예시:
17
17
@@ -64,31 +64,31 @@ let clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescr
64
64
65
65
## 비하인드 스토리
66
66
67
-
`[[Prototype]]`을 다룰 수 있는 방법은 다양합니다. 목표는 하나인데 목표를 이루기 위한 수단은 여러 가지이네요!
67
+
`[[Prototype]]`을 다루는 방법은 다양합니다. 목표는 하나인데 목표를 이루기 위한 수단은 여러 가지이죠.
68
68
69
69
왜 그럴까요?
70
70
71
71
역사적인 이유가 있습니다.
72
72
73
-
- 생성자 함수의 `"prototype"` 프로퍼티는 아주 오래전부터 그 기능을 수행하고 있었습니다.
74
-
- 그런데 2012년, 표준에`Object.create`가 추가되었죠. `Object.create`를 사용하면 주어진 프로토타입을 사용해 객체를 만들 수 있긴 하지만, 프로토타입을 얻거나 설정하는것은 불가능했습니다. 그래서 브라우저는 비표준 접근자인 `__proto__`를 구현해 언제나 프로토타입을 얻거나 설정할 수 있도록 하였죠.
75
-
- 이후 2015년에 `Object.setPrototypeOf`와 `Object.getPrototypeOf`가 표준에 추가되면서 `__proto__`와 동일한 기능을 수행할 수 있게 되었습니다. 그런데 이 시점엔 `__proto__`가 모든 곳에 구현되어 있어서 사실상 표준(de-facto standard)이 되어버렸죠. 표준의 부록 B(Annex B)에 추가되기도 하였습니다. 이 부록에 추가되면 브라우저가 아닌 환경에선 선택사항이라는것을 의미합니다.
73
+
- 생성자 함수의 `"prototype"` 프로퍼티는 아주 오래전부터 사용되고 있었습니다.
74
+
- 그런데 2012년, 명세서에`Object.create`가 추가되었습니다. `Object.create`를 사용하면 주어진 프로토타입을 사용해 객체를 만들 수 있긴 하지만, 프로토타입을 얻거나 설정하는것은 불가능했습니다. 그래서 브라우저는 비표준 접근자인 `__proto__`를 구현해 언제나 프로토타입을 얻거나 설정할 수 있도록 하였습니다.
75
+
- 이후 2015년에 `Object.setPrototypeOf`와 `Object.getPrototypeOf`가 표준에 추가되면서 `__proto__`와 동일한 기능을 수행할 수 있게 되었습니다. 그런데 이 시점엔 `__proto__`를 사용하는 곳이 너무 많아서 `__proto__`는 사실상 표준(de-facto standard)이 되어버렸죠. 이 내용은 명세서의 부록 B(Annex B)에 추가되어 있습니다. 부록 B의 내용은 브라우저 이외의 호스트 환경에선 선택사항이라는것을 의미합니다.
76
76
77
-
이런 이유 때문에 지금은 여러 방식을 원하는 대로 쓸 수 있게 된 것입니다.
77
+
이런 역사적인 이유 때문에 지금은 여러 방식을 원하는 대로 쓸 수 있게되었습니다.
78
78
79
-
그런데 "왜 `__proto__`가 함수 `getPrototypeOf/setPrototypeOf`로 대체되었을까?"라는 의문이 떠오를 수 있습니다. 흥미로운 질문이죠. 답은 `__proto__`가 왜 나쁜지 이해하면 얻을 수 있습니다. 아래 내용을 계속 읽으면서 답을 찾아봅시다.
79
+
이쯤되면 "왜 `__proto__`가 함수 `getPrototypeOf`, `setPrototypeOf`로 대체되었을까?"라는 의문이 떠오를 수 있습니다. 흥미로운 질문이죠. 답은 `__proto__`가 왜 나쁜지 이해하면 얻을 수 있습니다. 아래 내용을 계속 읽으면서 답을 찾아봅시다.
80
80
81
81
```warn header="속도가 중요하다면 기존 객체의 `[[Prototype]]`을 변경하지 마세요."
82
-
원한다면 언제나 `[[Prototype]]`을 얻거나 설정할 수 있습니다. 기술적인 제약이 있는 건 아니죠. 하지만 대개는 객체를 생성할 때만 `[[Prototype]]`을 설정하고 이후엔 수정하지 않습니다. `rabbit`이 `animal`을 상속받도록 설정하고 난 이후엔 이를 변경하지 않죠.
82
+
원한다면 언제나 `[[Prototype]]`을 얻거나 설정할 수 있습니다. 기술적 제약이 있는 건 아니죠. 하지만 대개는 객체를 생성할 때만 `[[Prototype]]`을 설정하고 이후엔 수정하지 않습니다. `rabbit`이 `animal`을 상속받도록 설정하고 난 이후엔 상속 관계를 잘 변경하지 않습니다.
83
83
84
-
자바스크립트 엔진은 이런 시나리오를 토대로 최적화되어 있습니다. `Object.setPrototypeOf`나 `obj.__proto__=`를 써서 프로토타입을 그때그때 바꾸는 연산은 객체 프로퍼티 접근 관련 최적화를 망치기 때문에 매우 느립니다. 그러므로 `[[Prototype]]`을 바꾸는 것이 어떤 결과를 초래할지 확실히 알거나 속도가 전혀 중요하지 않은 경우가 아니라면 `[[Prototype]]`을 바꾸지 마세요.
84
+
자바스크립트 엔진은 이런 시나리오를 토대로 최적화되어 있습니다. `Object.setPrototypeOf`나 `obj.__proto__=`를 써서 프로토타입을 그때그때 바꾸는 연산은 객체 프로퍼티 접근 관련 최적화를 망치기 때문에 성능에 나쁜 영향을 미칩니다. 그러므로 `[[Prototype]]`을 바꾸는 것이 어떤 결과를 초래할지 확실히 알거나 속도가 전혀 중요하지 않은 경우가 아니라면 `[[Prototype]]`을 바꾸지 마세요.
85
85
```
86
86
87
-
## '아주 단순한' 객체 [#very-plain]
87
+
## 아주 단순한 객체 [#very-plain]
88
88
89
-
알다시피 객체는 키-값 쌍을 저장할 수 있는 연관 배열입니다.
89
+
알다시피 객체는 키-값 쌍이 있는 연관 배열로도 사용할 수 있습니다.
90
90
91
-
그런데 커스텀 사전을 만드는 것과 같이 사용자가 직접 입력한 키를 가지고 객체를 만들다 보면 사소한 결함이 발견됩니다. 다른 문자열은 괜찮지만 `"__proto__"`는 키로 사용할 수 없다는 결함이죠.
91
+
그런데 커스텀 사전을 만드는 것과 같이 사용자가 직접 입력한 키를 가지고 객체를 만들다 보면 사소한 결함이 발견됩니다. 다른 문자열은 괜찮지만 `"__proto__"`라는 문자열은 키로 사용할 수 없다는 결함이죠.
92
92
93
93
예시를 살펴봅시다.
94
94
@@ -101,31 +101,31 @@ obj[key] = "...값...";
101
101
alert(obj[key]); // "...값..."이 아닌 [object Object]가 출력됩니다.
102
102
```
103
103
104
-
사용자가 프롬프트 창에 `__proto__`를 입력하면 값이 제대로 할당되지 않네요.
104
+
프롬프트 창에 `__proto__`를 입력하면 값이 제대로 할당되지 않는것을 확인할 수 있습니다.
105
105
106
-
`__proto__` 프로퍼티는 특별한 프로퍼티라는 것을 이미 알고 있기 때문에 그렇게 놀랄만한 일은 아니긴 합니다. `__proto__`는 항상 객체이거나 `null`이어야 하죠. 문자열은 프로토타입이 될 수 없습니다.
106
+
`__proto__` 프로퍼티는 특별한 프로퍼티라는 것을 이미 알고 있기 때문에 그렇게 놀랄만한 일은 아니긴 합니다. 참고로 `__proto__`는 항상 객체이거나 `null`이어야 합니다. 문자열은 프로토타입이 될 수 없습니다.
107
107
108
-
그런데 사실 이런 결과를 의도하면서 구현한 건 아닐 겁니다. 키가 무엇이 되었든, 키-값 쌍을 저장하려고 하는데 키가 `__proto__`일 때 값이 제대로 저장되지 않는 건 명백한 버그이죠.
108
+
개발자가 위 예시와 같은 코드를 작성할 땐 이런 결과를 의도하면서 구현하진 않았을 겁니다. 키가 무엇이 되었든, 키-값 쌍을 저장하려고 하는데 키가 `__proto__`일 때 값이 제대로 저장되지 않는 건 명백한 버그이죠.
109
109
110
-
위 예시에선 이 버그가 그리 치명적이진 않습니다. 그런데 할당 값이 객체일 때는 프로토타입이 바뀔 수 있다는 치명적인 버그가 발생할 수 있습니다. 프로토타입이 바뀌면 예상치 못한 일이 발생할 수 있기 때문입니다.
110
+
예시에선 이 버그가 그리 치명적이진 않습니다. 그런데 할당 값이 객체일 때는 프로토타입이 바뀔 수 있다는 치명적인 버그가 발생할 수 있습니다. 프로토타입이 바뀌면 예상치 못한 일이 발생할 수 있기 때문입니다.
111
111
112
-
개발자들은 대개 프로토타입이 중간에 바뀌는 시나리오는 배제한 채 개발을 진행합니다. 이런 고정관념 때문에 버그의 원인을 찾는 게 힘들어지죠. 프로토타입이 바뀐 것을 눈치채지 못하기 때문입니다. 서버 사이드에서 자바스크립트를 사용 중일 땐 이런 버그가 취약점이 되기도 합니다.
112
+
개발자들은 대개 프로토타입이 중간에 바뀌는 시나리오는 배제한 채 개발을 진행합니다. 이런 고정관념 때문에 프로토타입이 중간에 바뀌면서 발생한 버그는 그 원인을 쉽게 찾지 못합니다. 서버 사이드에서 자바스크립트를 사용 할 땐 이런 버그가 취약점이 되기도 합니다.
113
113
114
114
`toString`을 비롯한 내장 메서드에 할당을 할 때도 같은 이유 때문에 예상치 못한 일이 일어날 수 있습니다.
115
115
116
-
그렇다면 이런 문제는 어떻게 예방할 수 있을까요?
116
+
그럼 우리는 이런 문제를 어떻게 예방할 수 있을까요?
117
117
118
-
객체 대신 `맵`을 사용하면 모든 것이 해결됩니다.
118
+
객체 대신 `맵`을 사용하면 됩니다.
119
119
120
-
그런데 자바스크립트를 만든 사람들이 아주 오래전부터 이런 문제를 고려했기 때문에 `객체`를 써도 문제를 피할 수 있습니다. 어떤 방법이 있는지 알아봅시다.
120
+
그런데 자바스크립트를 만든 사람들이 아주 오래전부터 이런 문제를 고려했기 때문에 `객체`를 써도 문제를 예방할 수 있습니다. 객체를 써서 문제를 예방하는 방법을 알아봅시다.
121
121
122
122
아시다시피 `__proto__`는 객체의 프로퍼티가 아니라 `Object.prototype`의 접근자 프로퍼티입니다.
123
123
124
124

125
125
126
-
그렇기 때문에 `obj.__proto__`를 읽거나 쓸때는 이에 대응하는 getter·setter가 프로토타입에서 호출되고 `[[Prototype]]`을 가져오거나 설정합니다.
126
+
그렇기 때문에 `obj.__proto__`를 읽거나 쓸때는 이에 대응하는 getter, setter가 프로토타입에서 호출되고 `obj`는 `[[Prototype]]`을 통해 getter와 setter에 접근합니다.
127
127
128
-
이 절을 시작할 때 언급한 것처럼 `__proto__`는 `[[Prototype]]`에 접근하기 위한 방법이지`[[Prototype]]` 그 자체가 아닌 것이죠.
128
+
이 절을 시작할 때 언급한 것처럼 `__proto__`는 `[[Prototype]]`에 접근하기 위한 수단이지`[[Prototype]]` 그 자체가 아닌 것이죠.
129
129
130
130
이제 간단한 트릭을 써 객체가 연관 배열의 역할을 다 할 수 있도록 해보겠습니다.
131
131
@@ -146,9 +146,9 @@ alert(obj[key]); // "...값..."이 제대로 출력됩니다.
146
146
147
147
`Object.create(null)`로 객체를 만들면 `__proto__` getter와 setter를 상속받지 않습니다. 이제 `__proto__`는 평범한 데이터 프로퍼티처럼 처리되므로 버그 없이 예시가 잘 동작하게 됩니다.
148
148
149
-
이런 객체는 '아주 단순한(very plain)' 혹은 '순수 사전식(pure dictionary)' 객체라고 부릅니다. 일반 객체 `{...}` 보다 훨씬 단순하기 때문이죠.
149
+
이렇게 프로토타입이 없는 빈 객체는 '아주 단순한(very plain)' 혹은 '순수 사전식(pure dictionary)' 객체라고 부릅니다. 일반 객체 `{...}` 보다 훨씬 단순합니다.
150
150
151
-
아주 단순한 객체는 내장 메서드가 없다는 단점이 있습니다. `toString`같은 메서드를 사용할 수 없죠.
151
+
참고로 아주 단순한 객체는 내장 메서드가 없다는 단점이 있습니다. `toString`같은 메서드를 사용할 수 없습니다.
152
152
153
153
```js run
154
154
*!*
@@ -158,9 +158,9 @@ let obj = Object.create(null);
158
158
alert(obj); // Error: Cannot convert object to primitive value (toString이 없음)
159
159
```
160
160
161
-
연관 배열로 쓸 때는 이런 단점이 문제가 되진 않습니다.
161
+
객체를 연관 배열로 쓸 때는 이런 단점이 문제가 되진 않습니다.
162
162
163
-
객체 관련 메서드 대부분은 `Object.keys(obj)` 같이 `Object.something(...)`형태입니다. 이 메서드들은 프로토타입에 있는 게 아니기 때문에 '아주 단순한 객체'에도 사용할 수 있습니다.
163
+
객체 관련 메서드 대부분은 `Object.keys(obj)` 같이 `Object.something(...)`형태를 띕니다. 이 메서드들은 프로토타입에 있는 게 아니기 때문에 '아주 단순한 객체'에도 사용할 수 있습니다.
0 commit comments