* 자바스크립트는 프로토타입 기반 언어 ☞ 프로토타입을 이용한 상속을 지원
- 자바스크립트는 객체 지향 언어 & 함수형 프로그래밍도 가능한 멀티 패러다임 언어
- 자바스크립트에는 클래스란 개념이 없어 객체 생성이나 상속이 다른 언어와 다르고, 특히 OOP는 주로 프로토타입이란 매커니즘을 통해 이루어진다.
구분 | 프로토타입 기반 언어 | 클래스 기반 언어 |
예시 | JavaScript | Java, C++ |
객체 | (프로토타입)객체 | 클래스, 인스턴스 |
상속 |
프로토타입을 통한 상속 존재하는 객체와 존재하는 객체의 동적인 연결 |
클래스를 통한 상속 클래스의 상속 정보를 이용해 상속 구조의 모습을 가진 새로운 객체를 찍어내는 방법 |
(ref: 프로토타입 기반 프로그래밍 - 위키백과)
// (ref: https://webclub.tistory.com/162)
/*** 클래스 기반(일반 언어) - JAVA ***/
/* 1단계 : 클래스 정의 */
public class Prisoner {
public int setence = 4;
public int probation = 2;
public string name;
public string id;
/* 2단계 : 클래스 생성자 정의*/
public Prisoner(string name, string id) {
this.name = name;
this.id = id;
}
}
/* 3단계 : 객체 생성 */
Prisoner firstPrisoner = new Prisoner("Joe", "12A");
Prisoner secondPrisoner = new Prisoner("Sam", "2BC");
/*****************************************************/
/*** 프로토타입 기반(자바스크립트) - JAVASCRIPT ***/
/* 1단계 : 프로토타입 객체 정의 */
var proto = {
sentence : 2,
probation : 2
};
/* 2단계 : 객체 생성자 정의 */
var Prisoner = function(name, id) {
this.name = name;
this.id = id;
}
/* 3단계 : 생성자와 프로토타입 객체 연결 */
Prisoner.prototype = proto;
/* 4단계 : 객체 생성 */
var firstPrisoner = new Prisoner("Joe", "12A");
var secondPrisoner = new Prisoner("Joe", "12A");
1. 객체 템플릿 생성
- 클래스 기반 프로그래밍에서는 이를 클래스라 부르고, 프로토타입 기반 프로그래밍에서는 이를 프로토타입 객체라고 부른다.
----> 객체를 생성할 수 있는 프레임워크 역할
2. 생성자를 생성
- 클래스 기반 언어에서는 객체를 생성할 때 생성자가 어떤 클래스에 속하는지 명확히 알 수 있도록 클래스 내부에 정의
- 프로토타입 기반 언어에서는 객체 생성자를 프로토타입 외부에서 설정하고 있기 때문에 둘을 서로 연결해 주는 3단계 필요
3. 마지막으로 객체를 생성
(ref: https://webclub.tistory.com/162)
▶ Inheritance Patterns - ES5 Prototype
▷ Object(객체)
* 자바스크립트 객체 생성 방법
1. 객체 리터럴
2. 생성자
// 1. 객체리터럴
var objectMadeByLiteral = {};
// 2. 생성자
var objectMadeByConstructor = new Object();
•리터럴이나 생성자나 모두 객체의 내용이나 프로토타입의 구조 면에서 동일한 객체 생성
- 둘 다 Object 타입을 갖는 객체로 Object 타입의 메서드인 hasOwnProperty 나 toString, valueOf 등을 사용 가능
•생성된 객체는 Object 생성자의 프로토타입을 상속받은 객체
(실제로는 링크드리스트 형태의 참조를 통한 객체끼리의 연결에 가깝고 클래스 메커니즘처럼 정적이지 않고 매우 동적이다)
▷ Prototype(프로토타입)
•프로토타입을 이용하면 객체와 객체를 연결하고 한쪽 방향으로 상속을 받는 형태를 생성 가능
→ 객체와 객체를 연결해 멤버 함수나 멤버 변수를 공유 한다는 뜻
(이를 이용해 자바스크립트에서는 상속과 비슷한 효과를 얻는 것)
* Prototype Chain(프로토타입 체인)
: 프로토타입에서 상속을 구현하는 핵심 메커니즘; 객체와 객체의 연결을 통한 단방향 공유 관계 → __Proto__
상속 관점에서 자바스크립트의 유일한 생성자는 객체뿐
- 각각의 객체는 [[Prototype]]이라는 은닉(private) 속성을 가지는데 자신의 프로토타입이 되는 다른 객체 가리킨다.
그 객체의 프로토타입 또한 프로토타입을 가지고 있고 이것이 반복되다, 결국 null을 프로토타입으로 가지는 객체에서 끝난다. null은 더 이상의 프로토타입이 없다고 정의되며, 프로토타입 체인의 종점 역할을 한다.
(ref: https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain)
예제)
/* a 하고 b 라는 객체
a 에는 attr1 이라는 멤버변수, b 에는 attr2 라는 멤버변수가 있다
지금은 b 객체를 통해 a의 attr1 속성에 접근할 방법이 없다 */
var a = {
attr1: 1
}
var b = {
attr2: 2
}
- b 객체에서도 a의 attr1 을 마치 b가 소유한 것처럼 사용하길 원한다면 __proto__ 라는 특수한 속성을 이용
b.__proto__ = a;
b.attr1 // 'a1'
* __proto__ 속성
: ECMAScript의 스펙 [[Prototype]] 이 자바스크립트로 노출된 것
★ 개발 코드에서 직접적으로 접근하는 것은 피해야 한다!
__proto__ 이 참조하는 객체를 확인해야 하는 상황(예를 들면 프레임웍 개발)이라면 __proto__ 속성을 직접 사용하지 말고 Object.getPrototypeOf() 를 이용해서 참조하면 된다.
var a = {
attr1: 'a1'
}
var b = {
attr2: 'a2'
}
b.__proto__ = a;
b.attr1 // 'a1'
a.attr1 = 'a000'; // 상속받은 객체의 내용 변경
b.attr1 // 'a000'
a.attr3 = 'a3' // 상속받은 객체의 내용이 추가
b.attr3 // 'a3'
delete a.attr1 // 상속받은 객체의 내용이 삭제
b.attr1 // undefined
•b 의 __proto__ 속성이 a 객체를 참조 → a를 (a의 멤버 변수나 메서드를 몇 가지 제약은 있지만) 마치 b가 소유한 것처럼 사용할 수 있다는 것. 이 과정에서 상속과 비슷한 효과를 얻을 수 있게 된다.
•프로토타입을 통한 상속은 존재하는 객체와 존재하는 객체의 동적인 연결로 표현
→ 이미 객체가 만들어진 상태라도 상속된 내용이 변경되거나 혹은 추가되기도 하고 아예 상속 구조를 바꿀 수도 있게 된다.
* 프로토타입 식별자 룩업
: 프로토타입 체인을 통해 객체의 메서드나 속성을 찾아가는 과정
•프로토타입 체인을 통한 상속의 경우 메서드를 실행할 때 동적으로 해당 메서드를 찾아서 실행
→ 프로토타입 체인을 통한 상속의 경우 실행을 해봐야 객체가 해당 멤버를 가지고 있는지 알 수 있다.
var a = {
attr1: 'a'
};
var b = {
__proto__: a,
attr2: 'b'
};
var c = {
__proto__: b,
attr3: 'c'
};
c.attr1 // 'a'
- 객체를 세 개 만들고 각 객체의 __proto__ 속성을 이용해 c -> b-> a 로 연결
- c.attr1 로 c 객체에는 없는 attr1이라는 속성에 접근하면 자바스크립트 엔진은 아래와 같은 작업을 수행
-
c객체 내부에 attr1 속성을 찾는다. -> 없다.
-
c객체에 __proto__ 속성이 존재하는지 확인한다. -> 있다.
-
c객체의 __proto__ 속성이 참조하는 객체로 이동한다. -> b객체로 이동
-
b객체 내부에 attr1 속성을 찾는다. -> 없다.
-
b객체에 __proto__ 속성이 존재하는지 확인한다. -> 있다.
-
b객체의 __proto__ 속성이 참조하는 객체로 이동한다. -> a객체로 이동
-
a객체 내부에 attr1 속성을 찾는다. -> 있다.
-
찾은 속성의 값을 리턴한다.
※ 단일 링크드리스트 형태로 한쪽 방향의 연결(상속 개념 적용 가능)
→ c에서 a의 속성은 접근할 수 있지만 a에서 c의 속성은 접근할 수 없다.
* 메서드 오버라이드
* 생성자
: 생성자를 이용해 객체를 생성하면 생성된 객체는 생성자의 프로토타입 객체와 프로토타입 체인으로 연결
//constructor
function Parent(name) {
this.name = name;
}
Parent.prototype.getName = function() {
return this.name;
};
var p = new Parent('myName');
/* Parent 라는 생성자가 만들어내는 객체는 p 는 name 이라는 속성을 가지고 있고 getName 라는
프로토타입 메서드를 사용할 수 있다. p 객체가 Parent 의 프로토타입 메서드에 접근할 수 있는
이유는 p 객체의 __proto__ 속성이 Parent.prototype 을 가리키고 있기 때문이다.
-> 이 과정은 생성자를 new 키워드와 함께 사용할 때 엔진 내부에서 연결해준다. */
/************************************************************************/
var p = new Parent('myName');
// 엔진 내부에서 하는 일
p = {}; // 새로운 객체를 만들고
Parent.call(p, 'myName'); // call이용해 Parent함수의 this를 p로 대신해서 실행해주고
p.__proto__ = Parent.prototype; // 프로토타입을 연결한다.
p.getName(); // 'myName'
/************************************************************************/
- 생성자가 만든 객체(p)와 생성자의 prototype(Parent.prototype) 연결 과정; p -> Parent.prototype 탐색
※ 어떻게 Parent 타입을 상속받는 Child 타입 생성?
•Child 의 인스턴스 -> Child.prototype -> Parent.prorotype 프로토타입 체인의 연결 구조
- Child의 인스턴스 -> Child.prototype 으로의 연결은 Parent의 경우와 동일
- 중요한건 어떻게 Child.prototype과 Parent.prototype을 연결하는가
•자식의 프로토 타입과 부모의 프로토타입의 연결은 결국 객체와 객체의 연결
→ Object.create() 라는 표준 API를 이용한 방식
* Object.create()
: 객체를 인자로 받아 그 객체와 프로토타입 체인으로 연결되어 있는 새로운 객체 리턴
var a = {
attr1: 'a'
};
var b = {
__proto__: a,
attr2: 'b'
};
// (이 코드는 __proto__ 를 통해 프로토타입이 연결되는 과정을 설명하려고 작업한 실제로는 사용해선 안될 코드)
•Object.create()을 이용해__proto__속성에 직접 접근하지않고 프로토타입 체인 연결 가능
var a = {
attr1: 'a'
};
var b = Object.create(a);
b.attr2 = 'b';
/* Object.create() 으로 인해 __proto__ 가 a를 참조하는 새로운 빈객체를 리턴하게되고
b 에 참조되어 b.attr1 이런 식으로 a 객체의 맴버에도 접근할 수 있다. */
•자식 프로토타입과 부모 프로토타입이 연결
function Parent(name) {
this.name = name;
}
Parent.prototype.getName = function() {
return this.name;
};
function Child(name) {
Parent.call(this, name); // (※)
this.age = 0;
}
Child.prototype = Object.create(Parent.prototype); // (1)
Child.prototype.constructor = Child;
Child.prototype.getAge = function() {
return this.age;
};
var c = new Child(); // (2)
•(1) 에서 Object.create() 을 이용해 Child의 prototype 객체를 교체
- (1) 에서 만들어진 새 객체 Child.prototype 는 __proto__ 속성이 Parent.prototype 을 가르키게 된다.
•(2) 에서 Child의 인스턴스 c의 __proto__ 가 Child.prototype 을 참조
----> c -> Child.prototype -> Parent.prototype 으로 연결되는 프로토타입 생성(프로토타입 룩업시 우리가 의도한 탐색 경로로 식별자를 찾게 된다.)
(※ 추가로 Child 생성자에서 Parent 생성자를 빌려서 객체를 확장한 것은 생성자 빌려 쓰기라는 오래된 기법으로 자바스크립트 상속에서 부모의 생성자를 실행하는 유일한 방법이다. 이렇게 해야 부모타입의 생성자의 내용도 상속)
* Reference
http://www.ecma-international.org/ecma-262/6.0/#sec-terms-and-definitions-prototype
ECMAScript 2015 Language Specification – ECMA-262 6th Edition
www.ecma-international.org
https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain
상속과 프로토타입
Java 나 C++ 같이 클래스 기반의 언어를 사용하던 프로그래머는 자바스크립트가 동적인 언어라는 점과 클래스가 없다는 것에서 혼란스러워 한다. (ES2015부터 class 키워드를 지원하기 시작했으나, 문법적인 양념일 뿐이며 자바스크립트는 여전히 프로토타입 기반의 언어다.)
developer.mozilla.org
https://medium.com/@bluesh55/javascript-prototype-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-f8e67c286b67
[Javascript ] 프로토타입 이해하기
자바스크립트는 프로토타입 기반 언어라고 불립니다. 자바스크립트 개발을 하면 빠질 수 없는 것이 프로토타입인데요. 프로토타입이 거의 자바스크립트 그 자체이기때문에 이해하는 것이 어렵고 개념도 복잡합니다.
medium.com
* JavaScript prototype-based inheritance visualized https://github.com/rus0000/jsinheritance
https://meetup.toast.com/posts/104
쉽게 이해하는 자바스크립트 프로토타입 체인 : TOAST Meetup
쉽게 이해하는 자바스크립트 프로토타입 체인
meetup.toast.com
http://insanehong.kr/post/javascript-prototype/
Javascript 기초 - Object prototype 이해하기 | Insanehong's Incorrect Note
소개 이번 글에서 다룰 내용은 자바스크립트의 프로토타입 상속(prototypal inheritance) 이라는 확장과 객체의 재사용을 가능하게 해주며 class 기반으로 인스턴스를 생성하지 않는 자바스크립트에서 객체지향적인 개발 개념을 가질수 있게 해주는 Prototype에 대한 내용이다. 하지만 일반적으로 자바스크립트에 대한 정공파가 아니면 제대로 이해하고 있지 않거나 이해 하기 힘든 부분이기도 하다. 그이유는 Prototype Object 와 Prot
insanehong.kr
'CODESTATES > Immersive' 카테고리의 다른 글
DataStructure Reference (0) | 2020.01.09 |
---|---|
[TIL] 1/3: Inheritance Patterns - 3. ES6 Class 기반 상속/ super 키워드 (0) | 2020.01.05 |
[TIL] 1/3: Inheritance Patterns - 1. ES6/ ES6 new features (0) | 2020.01.03 |
[TIL] 12/31: Time Complexity (0) | 2019.12.31 |
[TIL] 12/30: Advanced Data Structure (0) | 2019.12.30 |