Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- 플로이드 워셜
- 뮤텍스란?
- 싸피
- 프록시서버
- Synchronization
- 뮤텍스
- 세마포어와 뮤텍스의 차이
- 싸피 면접 후기
- 세마포어와 뮤텍스
- 최단 경로
- floyd-warshall
- 플로이드 와샬
- 호스팅이란?
- 세마포어란?
- 다익스트라 알고리즘
- 다익스트라
- 서버 호스팅
- 삼성 청년 SW 아카데미
- 프록시
- Proxy
- 호스팅
- 싸피 합격
- SSAFY
- 세마포어
- Proxy Server
- 클라우드 서버
- 웹 호스팅
- 동기화
- Dijkstra Algorithm
Archives
- Today
- Total
어제의 나보다 성장한 오늘의 나
[자바스크립트] 프로토타입 체이닝 본문
간단한 개념
- 기존 C++이나 자바 같은 객체지향 프로그래밍 언어와는 다른 프로토타입 기반의 객체지향 프로그래밍을 지원한다.
- 자바스크립트에서는 클래스 개념이 없다. 대신 객체 리터럴이나 생성자 함수로 객체를 생성한다.
- 이렇게 생성된 객체의 부모 객체가 바로 '프로토타입' 객체다.
- 상속 개념과 마찬가지로 자식 객체는 부모 객체가 가진 프로퍼티 접근이나 메서드를 상속받아 호출하는 것이 가능
- 자바스크립트의 모든 객체는 자신의 부모인 프로토타입 객체를 가리키는 참조 형태의 숨겨진 프로퍼티가 있다.
- 암묵적 프로토타입 링크라고 부르며, 이러한 링크는 모든 객체의 __proto__ 프로퍼티에 저장된다.
여기서 주의해야 할점! 매우 매우 계속 얘기하는 강조되는 prototype 프로퍼티와 객체의 숨은 프로퍼티인 __proto__ 링크를 구분해야 한다는 점이다.
function Person(name){
this.name=name;
};
// Person() 생성자 함수로 생성된 foo 객체는 Person() 함수의 프로토타입 객체를 [[Prototype]]링크로 연결
var foo = new Person('foo');
console.dir(Person);
console.dir(foo);
- 자바스크립트 객체 생성 규칙 : 모든 객체는 자신을 생성한 생성자 함수인 prototype 프로퍼티가 가리키는 프로토 타입 객체를 자신의 부모 객체로 설정하는 __proto__ 링크로 연결한다.
- Person() 생성자 함수는 prototype 프로퍼티로 자신과 링크된 프로토타입 객체를 가리킨다.
- Person() 생성자 함수로 생성된 foo 객체는 Person() 함수의 프로토타입 객체를 __proto__ 링크로 연결한다.
- 결국 prototype 프로퍼티나 __proto__ 링크 같은 프로토타입 객체를 가리키고 있다.
- 객체를 생성하는 건 생성자 함수의 역할이지만, 생성된 객체의 실제 부모 역할을 하는 건 생성자 자신이 아닌 생성자의 prototype 프로퍼티가 가리키는 프로토타입 객체다.
- 결괏값을 잘 살펴보면, Person() 생성자 함수의 prototype 프로퍼티와 foo 객체의 __proto__ 프로퍼티가 같은 프로토타입 객체를 가리키고 있다는 알 수 있다.
- 위에서 __proto__를 숨겨진 프로퍼티라고 하였는데 크롬이나 파이어폭스에서는 명시적으로 제공하고 있다.
1. 객체 리터럴 방식으로 생성된 객체의 프로토타입 체이닝
- 객체는 자기 자신의 프로퍼티뿐만 아니라, 자신의 부모 역할을 하는 프로토타입 객체의 프로퍼티 또한 마치 자신의 것처럼 접근이 가능하다.
- 이것을 가능케 하는 게 바로 프로토타입 체이닝이다.
// 객체는 자기 자신의 프로퍼티뿐만이 아니라, 자신의 부모 역할을 하는 프로토타입 객체의 프로퍼티
// 또한 마치 자신의 것처럼 접근하는게 가능
var myObject = {
name:'foo',
sayName:function(){
console.log('My Name is '+this.name);
}
};
myObject.sayName();
console.log(myObject.hasOwnProperty('name')); // true
console.log(myObject.hasOwnProperty('nickName')); // false
// sayNickName() 메서드는 myObject의 메서드가 아니므로 에러가 발생한다.
myObject.sayNickName();
- myObject 객체에 hasOwnProperty() 메서드가 없음에도 결과가 정상적으로 출력됐다.
- 참고로 hasOwnProperty() 메서드는 이 메서드를 호출한 객체에 인자로 넘긴 문자열 이름의 프로퍼티나 메서드가 있는지 체크하는 API 함수다.
- name은 있으므로 true, nickname은 없으므로 false
왜? myObject 객체가 hasOwnProperty() 메서드가 없음에도 호출할 때는 에러가 발생하지 않을까?
- 객체 리터럴로 생성한 객체는 Object()라는 내장 생성자 함수로 생성된 것이다.
- Object() 생성자 함수도 함수 객체이므로 prototype이라는 프로퍼티 속성이 있다.
- 자바스크립트의 규칙으로 생성한 객체 리터럴 형태의 myObject는 아래 그림처럼 Object() 함수의 prototype 프로퍼티가 가리키는 Object.prototype 객체를 자신의 프로토타입 객체로 연결한다.
프로토타입 체이닝 개념을 살펴보자
- 먼저 객체 리터럴로 생성된 객체는 Object()라는 내장 생성자 함수로 생성된 것이다.
- 따라서 Object() 생성자 함수도 함수 객체이므로 prototype이라는 프로퍼티 속성이 있다.
- 자바스크립트에서 특정 객체의 프로퍼티나 메서드에 접근하려고 할 때, 해당 객체에 접근하려는 프로퍼티 또는 메서드가 없다면 __proto__ 링크를 따라 자신의 부모 역할을 하는 프로토타입 객체의 프로퍼티를 차례대로 검색하는 것을 포로토타입 체이닝이라고 말한다.
// 객체는 자기 자신의 프로퍼티뿐만이 아니라, 자신의 부모 역할을 하는 프로토타입 객체의 프로퍼티
// 또한 마치 자신의 것처럼 접근하는게 가능
var myObject = {
name:'foo',
sayName:function(){
console.log('My Name is '+this.name);
}
};
myObject.sayName(); // My Name is foo
console.log(myObject.hasOwnProperty('name')); // true
console.log(myObject.hasOwnProperty('nickName')); // false
// sayNickName() 메서드는 myObject의 메서드가 아니므로 에러가 발생한다.
myObject.sayNickName();
- sayName() 메서드의 결과는 제대로 됐지만 sayNickName() 메서드는 myObject의 메서드가 아니므로 에러가 발생한다.
- 그렇다면! hasOwnProperty() 메서드가 없음에도 결과가 정상적으로 출력된다는 것을 확인할 수 있다.
- 위에서 설명했다 시피 부모 역할을 하는 Object.prototype 프로토타입 객 내에 hasOwnProperty() 메서드가 있는지 검색한다.
- hasOwnProperty()는 Object.prototype 객체에 포함되어 있으므로 정상적으로 코드가 수행된다.
2. 생성자 함수로 생성된 객체의 프로토타입 체이닝
- 생성자 함수로 객체를 생성하는 경우는 객체 리터럴 방식과 약간 다른 프로토타입 체이닝이 이뤄진다.
- 하지만 두 가지 방식 모두 기본 원칙을 잘 지키고 있다.
- "자바스크립트에서 모든 객체는 자신을 생성한 생성자 함수의 prototype 프로퍼티가 가리키는 객체를 자신의 프로토타입 객체(부모 객체)로 취급한다."
function Person(name,age,hobby){
this.name=name;
this.age=age;
this.hobby=hobby;
};
var foo = new Person('foo',30,'tennis');
console.log(foo.hasOwnProperty('name')); // true
console.dir(Person.prototype);
- foo 객체에는 hasOwnProperty 메서드가 없다
- 프로토타입 체이닝 => foo 객체의 부모 객체인 Person.prototype에서 찾는다.
- prototype 프로퍼티에서 알아 봤듯이 함수에 연결된 프로토타입 객체는 디폴트로 constructor프로퍼티만을 가진 객체이므로 hasOwnProperty() 메서드는 없다.
- 왜 true가 나왔을까?
- Person.prototype는 Object.prototype을 프로토타입 객체이다.
- Object.prototype 객체의 hasOwnProperty() 메서드가 실행되므로 에러가 발생하지 않고 true가 출력된다.
3. 프로토타입 체이닝의 종점
- 자바스크립트에서 Object.prototype 객체는 프로토타입 체이닝의 종점이다.
- 이것을 달리 해석하면, 객체 리터럴 방식이나 생성자 함수 방식에 상관없이 모든 자바스크립트 객체는 프로토타입 체닝으로 Object.prototype 객체가 가진 프로퍼티와 메서드에 접근하고, 서로 공유가 가능하다는 것을 알 수 있다.
4. 기본 데이터 타입 확장
- 자바스크립트의 숫자, 문자열, 배열 등에서 사용되는 표준 메서드들의 경우, 이들의 프로토타입인
Number.prototype, String.prototype, Array.prototype 등에 정의되어 있다. - 물론 이러한 기본 내장 프로토타입 객체 또한 Object.prototype을 자신의 프로토타입으로 가지고 있어서 프로토타입 체이닝으로 연결된다.
- 프로토타입 객체에도 사용자가 직접 정의한 메서드들을 추가하는 것을 허용한다.
// 문자열의 표준 빌트인 프로토타입 객체 String.prototype에 testMethod()를 추가
String.prototype.testMethod = function(){
console.log('This is the String.prototype.testMethod()');
}
var str = 'this is test';
// 프로토타입 체이닝으로 String.prototype에 정의한 testMethod()가 호출하는 것이 가능
str.testMethod();
console.log(String.prototype);
5. 프로토타입도 자바스크립 객체
- 프로토타입 객체 역시 자바스크립트 객체이므로 일반 객체처럼 동적으로 프로퍼티를 추가/삭제가 가능하다.
function Person(name){
this.name=name;
};
var foo = new Person('foo');
// foo.sayHello();
// 동적으로 sayHello() 추가
Person.prototype.sayHello = function(){
console.log('Hello');
}
foo.sayHello(); // Hello
- foo.sayHello()를 실행하면 메서드가 정의되어 있지 않아 에러가 발생한다.
- foo 객체의 포로토타입 객체인 Person.prototype 객체에 동적으로 sayHello() 메서드를 추가한다.
- foo객체에는 없지만, 프로토타입 체이닝으로 Person.prototype 객체에서 sayHello() 메서드를 검색한다.
6. 프로토타입 메서드와 this 바인딩
- 프로토타입 객체는 메서드를 가질 수 있다. ( 짧게 프로토 타입 메서드라고 부를 것이다. )
- 만약 프로토타입 메서드 내부에서 this를 사용한다면?
- 결국 메서드 호출 패턴에서의 this는 그 메서드를 호출한 객체에 바인딩된다!
function Person(name){
this.name=name;
};
Person.prototype.getName=function(){
return this.name;
};
var foo = new Person('foo');
console.log(foo.getName()); // 출력값 foo
// Person.prototype 객체에 name 프로퍼티를 동적으로 추가하고 person을 저장 했으므로 this.name은 ‘person’이 출력된다.
Person.prototype.name = 'person';
console.log(Person.prototype.getName()); // 출력값 person
- Person.prototype 객체에 getName() 메서드를 작성, 이 메서드 코드 내부는 this를 포함하고 있다.
- foo 객체에서 getName() 메서드를 호출하면 foo 객체에서 getName()을 찾을 수 없으므로 프로토타입 체이닝이 발생한다.
- 이때 getName() 메서드를 호출한 객체는 foo이므로 this는 foo 객체에 바인딩됨으로 foo가 출력된다.
- 그렇다면 Person.prototype 객체에 접근해서 getName() 메서드를 호출하면 어떻게 될까?
- 이때 getName() 메서드를 호출한 객체가 Person.prototype이므로 this도 여기에 바인딩된다.
- 그리고 name은 동적으로 'person'을 저장했으므로 'person' 출력
7. 디폴트 프로토타입은 다른 객체로 변경이 가능
- 디폴트 프로토타입 객체는 함수가 생성될 때 같이 생성되며, 함수의 prototype 프로퍼티에 연결된다.
- 이렇게 함수를 생성할 때 해당 함수와 연결되는 디폴트 프로토타입 객체를 다른 일반 객체로 변경하는 것이 가능하다.
- 이러한 특징을 이용해서 객체지향의 상속을 구현한다.
여기서 주의할 점!!! 생성자 함수의 프로토 타입 객체가 변경되면, 변경된 시점 이후에 생성된 객체들은 변경된 프로토타입 객체로 __proto__ 링크를 연결한다는 점을 기억해야 한다.!!!!
이에 반해 생성자 함수의 프로토타입이 변경되기 이전에 생성된 객체들은 기존 프로토타입 객체로의 __proto__링크를 그대로 유지한다.
//Person() 생성자 함수
function Person(name){
this.name = name;
}
// Person.prototype.constructor는 Person()생성자 함수를 가리킨다.
console.log(Person.prototype.constructor);
//foo 객체 생성
var foo = new Person('foo');
console.log(foo.country); // undefined
// 디폴트 프로토타입 객체 변경
// 생성자 함수의 프로토타입 프로퍼티를 변경하게 되면 이 후 생성되는 객체들은 변경된 프로토링크를 지니게 된다.
// 또한 변경 이전 링크는 그대로 유지된다.
Person.prototype = {
country : 'korea'
};
console.log(Person.prototype.constructor);
// bar 객체 생성
var bar = new Person('bar');
console.log(foo.country); // undefined
console.log(bar.country); // korea
console.log(foo.constructor); // Person() 생성자 함수
console.log(bar.constructor); // Object()
8. 객체의 프로퍼티 읽기나 메서드를 실행할 때만 프로토타입 체이닝 동작
- 객체의 특정 프로퍼티를 읽으려고 할 때, 프로퍼티가 해당 객체에 없는 경우 프로토타입 체이닝이 일어난다.
- 반대로 객체에 있는 특정 프로퍼티에 값을 쓰려고 한다면 이때 프로토타입 체이닝은 일어나지 않는다.
- 이는 당연한 얘기로, 자바스크립트는 객체에 없는 프로퍼티에 값을 쓰려고 할 경우 동적으로 객체에 프로퍼티를 추가하기 때문이다.
function Person(name){
this.name;
}
Person.prototype.country='korea';
var foo = new Person('foo');
var bar = new Person('bar');
// foo의 프로토타입 객체인 Person.prototype의 country프로퍼티 값
console.log(foo.country); // korea
console.log(bar.country); // korea
// foo객체에 country 프로퍼티 값이 동적으로 생성
foo.country='USA';
// 프로토타입 체이닝 없이 USA 출력
console.log(foo.country); // USA
// 프로토타입 체이닝을 거쳐 korea 출력
console.log(bar.country); // korea
- foo.country = 'USA'를 하게 되면 Person.prototype.country의 값이 USA로 안 바뀌고 새로 country 프로퍼티가 생성되는 것을 확인할 수 있다.
출처
Comments