·자바스크립트는 프로토타입 기반 언어
- 클래스 기반 언어는 상속을 사용(프로그래밍 언어 대부분이 클래스 기반)
- 프로토타입 기반 언어는 어떤 객체를 원형으로 삼고, 이를 복제(참조)하여 상속과 비숫한 효과를 얻음
1. 프로토타입의 개념 이해
1) constructor, prototype, instance의 관계
- 코드와 코드를 도식화한 그림
var instance = new Constructor();

- 생성자 함수 Constructor()를 new 연산자와 함께 호출
- Constructor에서 정의된 내용을 바탕으로 새로운 인스턴스(var instance)가 생성됨
- instance에는 __proto__라는 프로퍼티가 자동으로 부여되며, 이 프로퍼티는 Constructor의 prototype 프로퍼티를 참조
- prototype은 객체이며, 이를 참조하는 __proto__ 또한 객체
- prototype 내부에는 instance가 사용할 메서드 저장, instance에서도 __proto__를 통해 이 메서드들에 접근 가능
- __proto__를 사용한 접근 대신 Object.getPrototypeOf(instance) 또는 Refelect.getPrototypeOf(instance)를 통해 접근 권장
- 예시
// Person이라는 생성자 함수
var Person = function (name) {
this._name = name;
};
// Person의 prototype에 getname이라는 메서드 지정
Person.prototype.getName = function() {
return this._name;
};
// 생성자 함수 Person을 통해 새로운 인스턴스 name을 생성
var name = new Person('name');
// Person의 인스턴스 name은 __proto__프로퍼티를 통해 getName 메서드 호출 가능
name.__proto__.getName(); // undefined
// 인스턴스의 __proto__는 Person의 prototype을 참조하므로, 결국 둘은 같은 객체를 바라봄
Person.prototype = name.__proto__ // true
- name.__proto__.getName()이 undefined로 뜬 것은 getName()함수가 this._name을 반환하는데 this._name이 없기 때문
- 메서드의 this는 메서드 바로 앞의 객체이므로 이때 this는 name.__proto__
- 따라서, name.__proto__에 _name 프로퍼티를 지정해주면 정상적으로 getName()함수가 실행될 것
var name = new Person('name');
name.__proto__._name = 'NAME__proto__';
name.__proto__.getName(); // NAME__proto__
- __proto__는 생략 가능한 프로퍼티
var name1 = new Person('name1', 25);
name1.getName(); // name1
var name2 = new Person('name2', 25);
name2.getName(); // name2
- __proto__를 생략하고 메서드를 호출하면 메서드(getName())의 this는 name.__proto__가 아닌 name이 되어 바로 name._name을 return할 수 있음
- 도식화하면 다음과 같이 됨

- prototype과 __proto__
var Constructor = function (name) {
this.name = name;
};
Constructor.prototype.method1 = function() {};
Constructor.prototype.property1 = 'Constructor Prototype Property';
var instance = new Constructor('Instance');
console.dir(Constructor);
console.dir(instance);

- Constructor의 디렉터리 구조
→ f Constructor(name): Constructor라는 이름의 함수, name을 인자로 가진다는 의미
→ 옅은 색의 arguments, caller, length, name, prototype은 열거할 수 없는(접근할 수 없는) 프로퍼티
→ 짙은 색의 method1, property1은 직접 지정하였고, 열거할 수 있는 프로퍼티
- instance의 디렉터리 구조
→Constructor가 가장 먼저 출력되어 생성자 함수 Consturctor에 의해 생성된 것임을 명시
→ [[Prototype]]은 __proto__와 같은 것으로, 이는 Constructor.prototype을 참조하므로 같은 내용을 출력
- 내장 생성자 함수 Array
var arr = [1, 2];
console.dir(arr);
console.dir(Array);


- arr의 디렉터리 구조
→ Array(2): length가 2인 Array
→ 인덱스 0과 1이 짙은 색으로, 접근 가능하다는 것을 의미
→ length는 옅은 색
→ [[Prototype]](=__proto__): 옅은 색상의 다양한 메서드(원래 배열에 사용하는 다양한 메서드들이 출력되어 있음)
- Array의 디렉터리 구조
→ 함수라는 의미의 f
→ 함수의 기본적인 프로퍼티인 concat 등이 출력됨
→ prototype을 열면 arr의 __proto__와 같은 메서드들이 출력됨

- Array의 prototype 내부에 있는 메서드들에는 Array 생성자 함수를 통해 생성된 arr 같은 인스턴스도 얼마든지 접근 가능
- Array의 prototype 외부에 있는 from, isArray 등의 메서드에는 인스턴스(arr)가 직접 호출할 수 없고 Array 생성자 함수에서 직접 접근해야 실행 가능
var arr = [1, 2];
arr.forEach(function (){}); // (0), forEach는 Array의 prototype 내부에 존재하므로 인스턴스인 arr도 사용가능
Array.isArray(arr); // (0) true, isArray는 Array의 메서드이므로 Array가 사용가능
arr.isArray(); // (x) TypeError: arr.isArray is not a function, isArray는 Array의 prototype 외부에 있으므로 인스턴스인 arr은 사용 불가능
2) constructor 프로퍼티
- 생성자 함수의 prototype 내부에는 constructor라는 프로퍼티가 항상 존재
- 생성자 함수의 prototype을 참조하는 인스턴스의 __proto__에도 동일하게 constructor 프로퍼티가 항상 존재
- constructor 프로퍼티는 원래의 생성자 함수를 참조
- 인스턴스로부터 그 원형이 무엇인지 알기 위해 사용
var arr = [1, 2];
Array.prototype.constructor === Array
arr.__proto__.constructor === Array
arr.constructor === Array // __proto__는 생략 가능한 프로퍼티
var arr2 = new arr.constructor(3, 4);
console.log(arr2); // [3, 4]
- arr 인스턴스는 Array라는 생성자 함수로부터 생성되었으며, constructor 프로퍼티를 통해 이를 확인 가능
- arr.constructor는 생성자 함수 Array를 가리키므로 이를 활용해 새로운 인스턴스를 생성할 수도 있음
- constructor는 읽기 전용 속성이 부여된 Number, String, Boolean을 제외하고는 값을 바꿀 수 있음
var NewConstructor = function() {
console.log('this is new constructor!');
};
var dataTypes = [
1, // Number & false
'test', // String & false
true, // Boolean & false
{}, // NewConstructor & false
[], // NewConstructor & false
function(){}, // NewConstructor & false
/test/, // NewConstructor & false
new Number(), // NewConstructor & false
new String(), // NewConstructor & false
new Boolean(), // NewConstructor & false
new Object(), // NewConstructor & false
new Array(), // NewConstructor & false
new Function(), // NewConstructor & false
new RegExp(), // NewConstructor & false
new Date(), // NewConstructor & false
new Error() // NewConstructor & false
];
dataTypes.forEach(function(d) {
d.constructor = NewConstructor;
console.log(d.constructor.name, '&', d instanceof NewConstructor);
});
→ Number인 1, String인 'test', Boolean인 true는 d.constructor = NewConstructor에 의해 값이 바뀌지 않음
→ 다른 데이터타입들은 NewConstructor로 constructor가 변경됨
→ d instanceof NewConstructor는 d가 NewConstructor라는 생성자 함수에 의해 생성된 것인지 여부를 출력
→ d instanceof NewConstructor가 전부 false로 나왔으므로, constructor가 변경되어도 데이터 타입이나 인스턴스의 원형(인스턴스를 생성한 생성자 함수)이 바뀌는 것은 아님
- 예시
var Person = function (name) {
this.name = name;
};
var p1 = new Person('사람1');
var p1Proto = Object.getPrototypeOf(p1);
var p2 = new Person.prototype.constructor('사람2');
var p3 = new p1Proto.constructor('사람3');
var p4 = new p1.__proto__.constructor('사람4');
var p5 = new p1.constructor('사람5');
[ p1, p2, p3, p4, p5 ].forEach(function (p) {
console.log(p, p instanceof Person);
});
// { name: "사람1"} true
// { name: "사람2"} true
// { name: "사람3"} true
// { name: "사람4"} true
// { name: "사람5"} true
→ p1은 생성자 함수([Constructor])인 Person에 의해 직접 생성
→ p2를 생성한 Person.prototype.constructor는 생성자 함수 자기 자신의 constructor, 즉 생성자 함수([Constructor])를 가리킴
→ p3를 생성한 p1Proto는 Object.getPrototypeOf(p1)은 p1의 __proto__와 같고 p1.__proto__.constructor는 p1을 생성한 생성자 함수([Constructor])를 가리킴
→ p4를 생성한 p1.__proto__.constructor는 위와 같은 의미
→ p5를 생성한 p1.constructor는 위에서 생략 가능한 프로퍼티인 __proto__를 생략한 것
- 정리하면, 다음 각 줄은 모두 같은 대상을 가리킴
[Constructor]
[instance].__proto__.constructor
[instance].constructor
Object.getPrototypeOf([instance]).constructor
[Constructor].prototype.constructor
- 또한, 다음 각 줄은 모두 같은 객체(prototype)에 접근 가능
[Constructor].prototype
[instance].__proto__
[instance]
Object.getPrototypeOf([instance])
'Front-end > Javascript' 카테고리의 다른 글
[코어 자바스크립트] 07. 클래스(1) (0) | 2022.12.29 |
---|---|
[코어 자바스크립트] 06. 프로토타입(2) (1) | 2022.12.27 |
[코어 자바스크립트] 05. 클로저(3) (0) | 2022.12.21 |
[코어 자바스크립트] 05. 클로저(2) (0) | 2022.12.21 |
[코어 자바스크립트] 05. 클로저(1) (0) | 2022.12.20 |