일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 리액트
- Node
- web
- graphQL
- 개발자
- 영화
- Redux
- 프로그래밍
- nodejs
- 웹개발
- 주짓수
- REACT
- 솔로드릴
- 하프가드
- JavaScript
- 파이썬
- 디자인패턴
- 영화감상
- 주짓떼로
- 드릴
- git
- 엄티로드
- Express
- 자바스크립트
- 영화리뷰
- 노드
- development
- 주짓떼라
- 개발
- 클로즈가드
- Today
- Total
As i wish
[You don't know JS] - 프로토타입 본문
이번에는 5장인 프로토타입에 대하여 공부해 보겠습니다!
1. [[Prototype]]
명세에 따르면 자바스크립트 객체는 [[Prototype]] 이라는 내부 프로퍼티가 있고 다른 객체를 참조하는 단순 레퍼런스로 사용합니다. 드물긴 하지만 [[Prototype]] 링크가 텅 빈 객체도 있긴합니다.
var myObject = {
a: 2
}
myObject.a // 2
위와 같이 객체의 프로퍼티가 있으면 [[Get]]을 이용하여 찾아서 출력하지만, 만약 a라는 프로퍼티가 없으면 다음은 이 객체의 [[Prototype]] 링크로 찾아서 확인 합니다.
var anotherObject = {
a: 2
};
var myObject = Object.create(anotherObject)
myObject.a // 2
위 처럼 myObject 는 anotherObject 와 [[Prototype]] 이 링크 되었기 때문에 myObject 에 a 라는 프로퍼티가 없으면 anotherObject 에서 찾아서 반환 합니다. 만약 여기에도 없다면 동일한 프로퍼티 명이 나올 때 까지 [[Prototype]] 링크를 따라 올라갑니다. 그래도 없다면 undefined 를 반환 합니다.
1.1 Object.prototype
[[Prototype]] 연쇄가 끝나는 지점은 내장 프로토 타입 Object.prototype에서 끝납니다. 익숙한 .toString(), 이나 .valueOf() 도 다 Object.prototype 프로퍼티라고 생각하시면 될 듯 하네요.
2.1 프로퍼티 세팅과 가려짐
myObject.foo = 'bar'
위 코드 같이 프로퍼티가 myObject에 직속되는 경우라면 문제가 없지만 만약 앞서 설명한것 처럼 타고 올라가서 상위 [[Prototype]]의 어딘가에 존재하면 약간 다른 일이 벌어집니다. 만약 foo가 myObject객체와 이 객체를 기점으로 한 [[Prototype]] 연쇄의 상위 수준 두곳에서 동시에 발견되면 이를 '가려짐' 이라고 합니다.
var anotherObject = {
a: 2
};
var myObejct = Object.create(anotherObject)
anotherObject.a; // 2
myObejct.a; // 2
anotherObject.hasOwnProperty('a') // true
myObejct.hasOwnProperty('a') // false
myObejct.a ++ // 가려짐 현상 발생
anotherObject.a // 2
myObejct.a // 3
myObejct.hasOwnProperty('a') // true
위 코드 처럼 anotherObject 에 a 프로퍼티는 가려짐 현상이 발생하게 됩니다.
2. 클래스
앞서 설명 한 대초 자바스크립트는 여타 클래스 지향 언어에서 제공하는 클래스라는 추상화된 패턴이나 설계가 전혀 없습니다. 다만 객체만 있을 뿐이죠.
2.1 클래스 함수
function Foo() {
/*
...
*/
}
Foo.prototype // {/* ... */}
위 처럼 모든 함수가 기본으로 프로토타입이라는 프로퍼티를 가지게 됩니다.
function Foo() {
/*
...
*/
}
var a = new Foo();
Object.getPrototypeOf(a) === Foo.prototype // true
따라서 new Foo() 로 생성한 a는 Foo.prototype이 가리키는 객체를 내부 [[Prototype]] 과 연결되 있다는 것을 확인 해 볼 수 있죠. 앞선 포스팅에서 언급 했듯이 자바스크립트는 실체 객체를 복사하는 것이 아니라 어떤 공용 객체에 [[Prototype]] 으로 연결된 객체를 생성만 하는 것이죠. 즉 , 복사가 아닌 위임 또는 연결이죠.
2.2 생성자
function Foo() {
// ...
}
Foo.prototype.constructor === Foo // true
var a = new Foo();
a.constructor === Foo // true
Foo.prototype 객체에는 기본적으로 열거 불가한 공용 프로퍼티 .constructor 가 있는데요 이는 객체 생성과 관련된 함수 (Foo) 를 다시 참조하기 위한 레퍼런스입니다. 또한 마찬가지로 생성자 호출로 생성한 객체 a 도 .constructor 프로퍼티를 갖고 있어서 '자신을 생성한 함수'를 가리킬 수 있게 됩니다.
2.3 체계
function Foo(name) {
this.name = name;
}
Foo.prototype.myName = function() {
return this.name;
}
var a = new Foo('a');
var b = new Foo('b');
a.myName(); // a
b.myName(); // b
Foo.prototype 객체의 프로퍼티/함수 가 a, bㅅ 생성 시 각각의 객체로 복사될 것 같지만 사실 그게 아니다. 앞서 언급한 것 처럼 객체에 직속된 레퍼런스가 발견되지 않으면 [[Prototype]] 링크를 찾아서 반환 하는데 a, b 생성 직후 각자의 내부 [[Prototype]]이 Foo.prototype에 링크가 되기 때문에 myName() 은 a, b가 아닌 Foo.prototype 에서 찾아서 반환 되는 것이죠.
function Foo() {
// ...
}
Foo.prototype = {
// ...
} // Set new prototype object
var a1 = new Foo();
a1.constructor === Foo // false;
a1.constructor === Object // true
한가지 특이한 사실은 Foo.prototype의 .constructor 프로퍼티는 기본으로 선언된 Foo 함수에 의해 생성된 객체에서만 존재 합니다. 새롭게 .prototype 객체를 생성하게 되면 .constructor 는 따라다니지 않죠. 따라서 위 코드도 a1.constructor 가 [[Prototype]] 연쇄에 의해 따라 올라가다가 연쇄의 끝자락인 Object.prototype 만나게 되서 Object 를 가리키게 되는거죠.
그렇기 때문에 .prototype을 새롭게 정의하게 되면 이와 관련되있던 코드들은 문제가 생길 수 있으니, 다른 프로퍼티들도 다 재정의 해줘야 합니다.
3. 프로토타입 상속
function Foo(name) {
this.name = name
}
Foo.prototype.myName = function() {
return this.name;
}
function Bar(name, label) {
Foo.call(this, name);
this.label = label;
}
// 'Bar.prototype'를 'Foo.prototype'에 연결한다.
Bar.prototype = Object.create(Foo.prototype);
// 앞서 언급했던것 처럼 'Bar.prototype' 과 연결된 프로퍼티는 일일이 해결 해야한다.
Bar.prototype.myLabel = function() {
return this.label;
}
var a = new Bar('a', 'obj a');
a.myName() // a
a.myLabel() // a
Bar() {} 함수를 선언하면 Bar는 여타 함수처럼 기본으로 .prototype 링크를 자신의 객체에 갖고 있습니다. 이 객체를 Foo.prototype 과 연결하고 싶은데, 현재 그렇게 연결되어 있지는 않습니다. 따라서 애초 연결된 객체와 헤어지고 Foo.prototype과 연결된 새로운 객체를 생성 (Bar.prototype = Object.create(Foo.prototype)) 하게 된거죠.
3.1 클래스 관계 조사
예를 들어 a 같은 객체가 어떤 객체로 위임할지는 어떻게 알 수 있을까요? 보통 전통적인 클래스 지향언어 에서는 인스턴스와 상속 계통을 살표보는 것을 인트로스펙션(리플렉션) 이라고 합니다.
function Foo() {
// ...
}
Foo.prototype.blah = '...';
var a = new Foo();
a instanceof Foo; // true
그러나 위와 달리 객체 두개 예를 들어 a, b 가 있다고 가정 했을 때에는 두 객체가 [[Prototype]] 연쇄를 통해 연결 되어 있는지는 전혀 알 수가 없죠.
function Foo() {
// ...
}
Foo.prototype.blah = '...';
var a = new Foo();
a instanceof Foo; // true
Foo.prototype.isPrototypeOf(a) // true
앞선 코드에 대안으로 위와 같이 해볼 수 있다. 위 코드는 Foo가 어떤 함수든 상관없이 다른 객체 테스트 시 사용할 객체 (Foo.prototype) 만 있으면 됩니다. isPrototypeOf() 는 'a 의 전체 [[Prototype]] 연쇄에 Foo.prototype이 있는가' 라는 질문에 대답해 줍니다.
b.isPrototypeOf(c);
c의 [[Prototype]] 연쇄 어딘가에 b가 존재하는가? 라는 물음 이죠.
4. 객체 링크
[[Prototype]] 체계는 다름 아닌 다른 객체를 참조하는 어떤 객체에 존재하는 내부 링크 입니다. 이 연결 고리는 객체의 프로퍼티/메서드를 참조하려고 하는데, 그런 프로퍼티/메서드 가 해당 객체에 존재하지 않을 때 주로 활용 됩니다. 자바스크립트 엔진은 [[Prototype]]에 연결된 객체를 하나씩 따라가면서 프로퍼티/메서드를 찾아보고 발견될 때 까지 같은 과정을 되풀이 하죠. 이렇게 객체사이에 형성된 일련의 링크를 '프로토타입 연쇄' 라고 합니다.
4.1 링크 생성
var foo = {
something: function() {
console.log('aaa');
}
}
var bar = Object.create(foo);
bar.something() // aaa
Object.create() 는 먼저 새로운 객체를 생성(bar) 하고 주어진 객체 (foo) 와 연결합니다. 이것만으로도 앞서 계속 했었던 클래스나 생성자 호출, .prototype 이나 .constructor 레퍼런스등을 동원한 함수로 쓸데 없이 골치 아프지 않게 됩니다.
두 객체에 의미 있는 관계를 맺어주는 데 클래스가 필수 인건 아니죠. 객체의 위임 연결만 신경써서 잘 처리하면 되는데, Object.create() 덕분에 한줄로 깔끔하게 할 수 있다는 것을 보여주게 된거죠.
function Foo() {
/*
...
*/
}
var a = new Foo();
var b = Object.create(Foo)
Object.getPrototypeOf(a) === Foo.prototype // true
Object.getPrototypeOf(b) === Foo // true
정리
- 객체에 존재하지 않은 프로퍼티를 접근하려고 시도 하면 [[Prototype]] 링크를 따라 수색 한뒤 프로퍼티를 찾아 반환 합니다.
- 모든 일반 객체의 최상위 프로토타입 연쇄에는 내장 Object.prototype 이 버티고 있습니다. 이 지점까지 이르러야 수색을 종료 합니다.
- 두 객체를 서로 연결 짓는 일반적인 방법은 new 키워드를 붙이는 것입니다.
- 새로운 객체와 손잡은 '다른 객체' 는 new를 이용하여 호출한 함수의 .prototype 이라고 임의로 명명한 프로퍼티를 통해 참조 됩니다.
- 자바스크립트는 전통적인 클래스 지향의 언어의 '클래스 인스턴스화 및 상속'과 유사해 보이지만 복사가 일어나지 않은 결정적 차이가 있습니다. 그러나 객체는 결국 다른 객체와 내부 [[Prototype]] 연쇄를 통해 연결되어 있죠.
- 사실 자바스크립트는 객체 간의 관계는 복사되는게 아니라 위임 연결이 조금 더 적절한 표현이라고 볼 수 있겠습니다.
'JavaScript' 카테고리의 다른 글
[You don't know JS] - 비동기성: 지금과 나중 (0) | 2019.06.15 |
---|---|
[You don't know JS] - 작동 위임 (0) | 2019.06.07 |
[You don't know JS] - 클래스와 객체의 혼합 (0) | 2019.05.21 |
[You don't know JS] - 객체 (0) | 2019.05.13 |
[You don't know JS] - this가 이런 거로군! - 2 (0) | 2019.05.11 |