•
자바스크립트는 프로토타입 기반 언어이다.
◦
클래스 기반 - 상속 사용
◦
프로토타입 기반 - 원형을 삼고 이를 복제
프로토타입 개념 이해
1. constructor, prototype, instance
•
흐름 이해하기
◦
어떤 생성자 함수 (Constructor)를 new 연산자와 함께 호출하면
◦
Constructor에서 정의된 내용을 바탕으로 새로운 instance가 생성
◦
instance는 __proto__ 라는 프로퍼티가 자동 부여
◦
이 프로퍼티는 Constructor의 prototype 프로퍼티를 참조
var Person = function (name) {
this._name = name;
};
Person.prototype.getName = function () {
return this._name;
};
JavaScript
복사
→ Person의 인스턴스는 __proto__ 프로퍼티를 통해 getName 호출이 가능하다.
var suzi = new Person('suzi');
suzi.__proto__.getName(); // undefined
// 왜 undefined?
// this가 suzi.__proto__를 가리킨다. (메서드로 호출됐을 때의 this)
Person.prototype === suzi.__proto__ // true
JavaScript
복사
◦
왜 getName 의 결과가 undefined 일까?
→ this 바인딩 대상이 잘못 지정되었다.
var suzi = new Person('suzi');
suzi.__proto__._name = 'suzi__proto__';
suzi.__proto__.getName(); // suzi__proto__
JavaScript
복사
◦
this를 suzi.__proto__ 가 아닌 인스턴스로 하는 방법은 무엇일까?
→ __proto__ 없이 인스턴스에서 곧바로 메서드를 사용하기
var suzi = new Person('suzi', 28);
suzi.getName(); // suzi
var iu = new Person('jieun', 28);
iu.getName(); // jieun
JavaScript
복사
◦
__proto__ 는 생략 가능한 프로퍼티
•
자바스크립트는 함수에 자동으로 객체인 prototype 프로퍼티를 생성
◦
해당 함수를 생성자 함수로 사용할 경우 → 인스턴스에 숨겨진 프로퍼티인 __proto__ 자동 생성
◦
__proto__ 프로퍼티는 생성자 함수의 prototype 프로퍼티 참조
◦
__proto__ 프로퍼티는 생략 가능하기 때문에 인스턴스는 생성자 함수의 prototype 의 메서드, 프로퍼티를 자신의 것처럼 접근 가능
var arr = [1, 2];
console.dir(arr);
console.dir(Array);
JavaScript
복사
•
__proto__ 에 Array의 메서드들이 들어있다.
•
프로토타입 메서드 vs 정적 메서드
◦
프로토타입 메서드 → 인스턴스가 마치 자신의 메서드처럼 호출할 수 있다.
◦
정적 메서드 → 인스턴스가 직접 호출이 불가능하다. 생성자 함수에서 직접 접근해야한다.
2. constructor 프로퍼티
•
constructor 프로퍼티는 원래의 생성자 함수(자기 자신)를 참조
→ 인스턴스로부터 그 원형이 무엇인지를 알 수 있는 수단
var arr = [1, 2];
Array.prototype.constructor = Array // true
arr.__proto__.constructor === Array // true
arr.constructor === Array // true
var arr2 = new arr.constructor(3, 4);
console.log(arr2); // [3, 4]
JavaScript
복사
•
constructor는 읽기 전용 속성이 부여된 예외적인 경우를 제외하고는 값을 바꿀 수 있다.
→ 참조하는 대상이 바뀔 뿐, 인스턴스의 원형이 바뀌지는 않는다.
// 다음은 모두 같은 대상을 가리킨다.
[Constructor]
[instance].__proto__.constructor
[instance].constructor
Object.getPrototypeOf([instance]).constructor
[Constructor].prototype.constructor
JavaScript
복사
프로토타입 체인
1. 메서드 오버라이드
•
정리 - prototype 개체를 참조하는 __proto__ 를 생략하면 인스턴스를 prototype 에 정의된 프로퍼티나 메서드를 자신의 것처럼 사용가능
Q: 인스턴스가 동일한 프로퍼티나 메서드를 가지고 있는 상황이라면?
var Person = function (name) {
this.name = name;
};
Person.prototype.getName = function () {
return this.name;
};
var iu = new Person('지금');
iu.getName = function () {
return '바로' + this.name;
};
console.log(iu.getName()); // 바로 지금
JavaScript
복사
•
메서드 오버라이드 → 메서드 위에 메서드를 덮어씌운것
•
자신의 프로퍼니 검색 → __proto__ 검색
console.log(iu.__proto__.getName()); // undefined
// -> Person의 prototype 객체에는 name 프로퍼티가 없기 때문
Person.prototype.name = '이지금';
console.log(iu.__proto__.getName()); // 이지금
// this가 prototype을 바라보고 있다 -> call, apply 사용
console.log(iu.__proto__.getName.call(iu)); // 지금
JavaScript
복사
•
결론 → 오버라이드된 경우에도 __proto__ 의 메서드에 우회적으로 접근이 가능하다.
2. 프로토타입 체인
•
모든 객체의 __proto__ 에는 Object.prototype 이 연결
•
프로토타입 체인
◦
어떤 데이터의 __proto__ 프로퍼티 내부에 다시 __proto__ 프로퍼티가 연쇄적으로 이어진 것
•
프로토타입 체이닝
◦
프로토타입 체인을 따라가며 검색하는 것
var arr = [1, 2];
Array.prototype.toString.call(arr); // 1, 2
Object.prototype.toString.call(arr); // [object Array]
arr.toString(); // 1, 2 (__proto__ 생략)
arr.toString = function () {
return this.join('_')l
};
arr.toString(); // 1_2 -> 메서드 오버라이딩 (자신의 메서드가 적용)
JavaScript
복사
•
arr.__proto__ 는 Array.prototype 을 참조
•
Array.prototype.__proto__ 는 Object.prototype 을 참
3. 객체 전용 메서드의 예외사항
•
어떤 생성자 함수이든 prototype 은 반드시 객체
→ Object.prototype 이 언제나 프로토타입 최상단에 존재
→ Object.prototype 의 메서드는 다른 데이터 타입도 사용할 수 있다.
•
객체만을 대상으로 동작하는 객체 전용 메서드는?
→ 정적 메서드로 부여
•
Object.prototype 에 접근할 수 없는 객체
→ Object.create(null) 은 __proto__ 가 없는 객체를 생성
4. 다중 프로토타입 체인
•
대각선의 __proto__ 를 연결해주면 된다.
◦
__proto__ 가 가리키는 대상인 생성자 함수의 prototype 이 연결하고자 하는 상위 생성자 함수의 인스턴스를 바라보게 한다.
var Grade = function () {
var args = Array.prototype.slice.call(arguments);
for (var i = 0; i < args.length; i++) {
this[i] = args[i];
}
this.length = args.length;
};
var g = new Grade(100, 80);
JavaScript
복사
◦
call apply 대신 배열 메서드를 직접 사용하게 하려면?
→ g.__proto__ , Grade.prototype 이 배열의 인스턴스를 바라보게 한다.
Grade.prototype = [];
JavaScript
복사