JavaScript는 프로토타입 기반 객체지향 프로그래밍 언어이다.
프로토타입 기반 객체지향 언어에서는 클래스 없이 객체 생성 가능
JavaScript의 모든 객체는 자신의 부모 객체와 연결되어 있다.
⇒ 마치 상속처럼 부모 객체의 프로퍼티와 메소드를 사용할 수 있다.
⇒ 이러한 부모 객체를 '프로토 타입 객체'라고 한다.
프로토 타입 객체는 생성자 함수에 의해 생성된 각각의 객체에 공유 프로퍼티를 제공하기 위해 사용
- JavaScript의 모든 객체는 [[Prototype]]라는 인터널 슬롯을 가진다.
- [[Prototype]]의 값은 null 또는 객체(부모 객체)이며, 상속을 위해 사용된다.
- [[Prototype]] 객체의 프로퍼티는 get 접근을 위해 상속되어 자식 객체의 프로퍼티처럼 사용
- set 접근은 허용하지 않는다.
- [[Prototype]]의 값은 프로토 타입이며, _ proto _ accessor property로 접근 가능
- 프로토타입은 객체 생성시 결정되며, 동적으로 변경할 수 있다.
[[Prototype]] vs prototype 프로퍼티
모든 객체는 [[Prototype]] 인터널 슬롯을 갖는다.
함수도 객체이기에 [[Prototype]] 인터널 슬롯을 갖으나, 일반 객체와는 다르게 prototype 프로퍼티도 갖는다.
[[Prototype]]
- 함수를 포함한 모든 객체가 갖고있는 인터널 슬롯
- 객체의 입장에서 자신의 부모객체를 가리키며, 함수 객체의 경우 Function.prototype를 가리킴
prototype 프로퍼티
- 함수 객체만 가지고 있는 프로퍼티
- 함수 객체가 생성자로 사용될 때, 이 함수를 통해 생성될 객체의 부모역할을 하는 객체를 가리킴
constructor 프로퍼티
- 프로토 타입 객체는 constructor 프로퍼티를 갖으며, 이는 객체의 입장에서 자신을 생성한 객체를 가리킨다
ex)
function Person(name) {
this.name = name;
}
var foo = new Person('Lee');
// Person() 생성자 함수에 의해 생성된 객체를 생성한 객체는 Person() 생성자 함수이다. console.log(Person.prototype.constructor === Person);
// foo 객체를 생성한 객체는 Person() 생성자 함수이다.
console.log(foo.constructor === Person);
// Person() 생성자 함수를 생성한 객체는 Function() 생성자 함수이다.
console.log(Person.constructor === Function);
Prototype chain
: JavaScript에서 해당객체에 찾고 있는 프로터티나 메소드가 없다면, [[Prototype]]이 가리키는 부모 객체의 프로퍼티나 메소드를 차례대로 검색하는 것
객체 리터럴로 생성된 객체의 prototype chain
- 객체 리터럴은 결국 Object() 생성자 함수의 객체 생성을 단순화 한것
⇒ 객체 리터럴로 객체를 생성한 경우, 프로토타입 객체는 Object.prototype이다.
ex)
var person = {
name: 'Lee',
gender: 'male',
sayHello: function(){
console.log('Hi! my name is ' + this.name);
}
};
console.dir(person);
console.log(person.__proto__=== Object.prototype); // ① true
console.log(Object.prototype.constructor === Object); // ② true
console.log(Object.__proto__=== Function.prototype); // ③ true
console.log(Function.prototype.__proto__=== Object.prototype); // ④ true
생성자 함수로 생성된 객체의 prototype chain
- 함수를 정의하는 3가지 방법 모두 결국 Function() 생성자 함수를 통해 함수 객체를 생성
⇒ 모든 함수 객체의 prototype 객체는 Function.prototype.
⇒ 생성자 함수도 함수 객체이므로 생성자 함수의 portotype 객체 역시 Function.prototype
▶ 객체 리터럴방식이나 생성자 함수 방식이나 결국은 모든 객체의 부모 객체인 Object.prototype에서 prototype chain이 끝나게 됨.
객체 생성 방식에 따른 prototype 객체
객체 생성 방식 | 엔진의 객체 생성 | 인스턴스트 prototype 객체 |
객체 리터럴 | Object() 생성자 함수 | Object.prototype |
Object() 생성자 함수 | Object() 생성자 함수 | Object.prototype |
생성자 함수 | 생성자 함수 | 생성자 함수이름.Function.prototype |
원시 타입(Primitive data type)의 확장
- 원시 타입은 객체가 아니므로 프로퍼티나 메소드를 가질수 없다.
- 객체의 프로토타입 객체 Object.prototype에 메소드를 추가하면 원시 타입, 객체 모두 메소드를 사용할 수 있다.
- Javascript는 표준 내장 객체의 프로토타입 객체에 개발자가 정의한 메소드의 추가를 허용한다.
ex)
var str = 'test';
String.prototype.myMethod = function() {
return 'myMethod';
}
console.log(str.myMethod()); // myMethod
console.dir(String.prototype); // [String: ''] { myMethod: [Function] }
console.log(str.__proto__=== String.prototype); // ① true
console.log(String.prototype.__proto__=== Object.prototype); // ② true
console.log(String.prototype.constructor === String); // ③ true
console.log(String.__proto__=== Function.prototype); // ④ true
console.log(Function.prototype.__proto__=== Object.prototype); // ⑤ true
프로토타입 객체의 변경
- 프로토 타입은 객체가 생성될 때 결정되며, 결정된 프로토 타입객체를 임의로 변경할 수 있다.
- 부모 객체를 동적으로 변경할 수 있다는 것이며, 이를 이용해 상속을 구현할 수 있다.
프로토타입 객체를 변경하면
- 변경 이전에 생성된 객체 : 기존 프로토 타입 객체를 [[Prototype]]에 바인딩
- 변경 이후에 생성된 객체 : 변경된 프로토 타입 객체를 [[Prototype]]에 바인딩
ex)
function Person(name) {
this.name = name;
}
var foo = new Person('Lee');
// 프로토타입 객체의 변경
Person.prototype = { gender: 'male' };
var bar = new Person('Kim');
console.log(foo.name); // 'Lee'
console.log(bar.name); // 'Kim'
console.log(foo.gender); // undefined
console.log(bar.gender); // 'male'
console.log(foo.constructor); // ① Person(name)
console.log(bar.constructor); // ② Object()
② : Person() 생성자 함수의 Prototype 프로퍼티가 가리키는 프로토타입 객체를 일반 객체로 변경하면서 Person.prototype.constructor 프로퍼티도 삭제
⇒ 프로토타입 체인에 의해 bar.constructor의 값은 프로토타입 체이닝에 의해 Object.prototype.constructor 즉 Object() 생성자 함수가 된다.
프로토타입 체인 동작 조건
-
객체의 프로퍼티를 참조할 때, 해당 객체에 프로퍼티가 없는 경우 동작
-
객체의 프로퍼티에 값을 할당하는 경우, 프로토타입 체인이 동작하지 않는다.
⇒ 객체에 해당 프로퍼티가 있는 경우, 값을 재할당하고
해당 프로퍼티가 없는 경우는 해당 객체에 프로퍼티를 동적으로 추가하기 때문
ex)
function Person(name) {
this.name = name;
}
Person.prototype.gender = 'male'; // ①
var foo = new Person('Lee'); var bar = new Person('Kim');
console.log(foo.gender); // ① 'male'
console.log(bar.gender); // ① 'male'
// 1. foo 객체에 gender 프로퍼티가 없으면 foo 객체에 프로퍼티 동적 추가
// 2. foo 객체에 gender 프로퍼티가 있으면 해당 프로퍼티에 값 할당
foo.gender = 'female'; // ②
console.log(foo.gender); // ② 'female'
console.log(bar.gender); // ① 'male'
'Programming Language > JavaScript' 카테고리의 다른 글
7. 스코프 (0) | 2021.01.24 |
---|---|
6. 객체 (0) | 2021.01.24 |
5. 함수 (0) | 2021.01.24 |
4. 제어문 (0) | 2021.01.24 |
3. 연산자 (0) | 2021.01.24 |