코어 자바스크립트를 꼼꼼하게 정독하고
복습하며 정리한 내용을 토대로 F-lab 멘토링을 받고있다.
이 글은 멘토링으로 알게된 것들을 다시한번 정리해보는 글이다.



사전지식1 - 객체지향 프로그래밍

객체지향 프로그래밍은 프로그램의 단위가 크고 복잡해지면서 모색된 방법이다.
레고 블럭과 같은 독립적 단위의 객체를 조립해 프로그램을 만드는 것과 같다.
객체는 실제 사물이나 개념의 특징을 나타내는 속성
그 속성의 상태값 및 상태값을 조작하는 동작을 묶은 복합적 자료구조라고 할 수 있다.

자바스크립트는 객체 기반의 언어이고, 자바스크립트를 이루는 거의 모든 것은 객체다.


사전지식2 - 상속

상속의 구현은 기존의 코드를 개사용하여 불필요한 중복을 줄일 수 있게 한다.
클래스 기반 언어에서는 클래스로 상속을 구현하지만 프로토타입 기반 언어인 자바스크립트는
원본 프로토타입을 만들어놓고 이를 복제(참조) 하여 상속과 비슷한 효과를 얻는다.


프로토타입

프로토타입 객체는 객체간 상속을 구현하기 위해 사용된다.
프로토타입은 어떤 객체의 부모역할을 하는 객체로, 자식객체에 공유 프로퍼티와 메서드를 제공한다.

1
const instance = new Constructor();

constructor 생성자 함수는 instance 객체를 생성하고
constructor가 가지고 있던 prototype의 속성을 참조하는 __proto__가 instance에 자동으로 부여된다.

prototype 객체 내부에는 메서드를 지정한다.
그러면 instance에서 숨겨진 __proto__를 통해 이 메서드에 접근할 수 있게 된다.

1
2
3
4
5
6
7
8
9
10
const Person = function (name) {
  this.name = name;
};
Person.prototype.getName = function () {
  return this.name;
};
// 생성자함수 내부에 메서드를 생성하면 생성자함수 자체가 메서드를 들고있게 되고
// prototype의 메서드로 선언하면 메서드는 숨겨진 상태로 사용할 수 있게 된다.

const suzi = new Person("suzi");

위와 같이 선언한 constuctor와 instance의 관계에서
Person.prototype == suzi.__proto__는 true가 된다.

1
suzi.getName(); // suzi


suzi 인스턴스에 숨겨진 proto 객체 내 getName 메서드를 실행하는 것이라서
suzi.__proto__.getName() 코드로 호출할 수도 있지만 이렇게 실행하면 메서드의 this는
suzi.__proto__가 되기 때문에 undefined가 반환된다.

자바스크립트는 __proto__를 생략 가능하게 만들어 놓았기 때문에 그냥 생략하고 사용하면 된다.

어쨌든 중요한 것은 생성자함수의 prototype에 정의된 메서드나 프로퍼티는
인스턴스에서도 자신의 것 처럼 접근할 수 있게 된다는 것이다.


prototype 내부의 constructor

prototype 객체 내부에는 constuctor라는 프로퍼티가 있다.
인스턴스의 __proto__ 내부도 마찬가지인데, 이것은 원래의 생성자함수 자체를 참조하며
인스턴스가 직접 constructor에 접근할 수 있는 수단이 된다.


기본 메서드를 사용할 수 있는 이유

1
2
3
const arr = [1, 2];
console.dir(arr);
console.dir(Array);

위 코드를 출력해보면 arr변수와 Array 생성자함수 내부를 볼 수 있다.
살펴보면 arr변수는 Array 생성자함수로부터 만들어졌기 때문에
Array 생성자함수의 prototype에 정의된 push pop slice find filter 등의
배열 메서드의 사용이 가능해진다는 것을 알 수 있다.


메서드 오버라이드

인스턴스는 prototype에 정의된 프로퍼티나 메서드를 자신의 것 처럼 사용할 수 있다.
그런데 만약 인스턴스가 동일한 이름의 프로퍼티나 메서드를 가지고 있다면?


1
2
3
4
5
6
7
8
9
10
11
12
13
const Person = function (name) {
  this.name = name;
};
Person.prototype.getName = function () {
  return this.name;
};

const jessie = new Person("jessie");
jessie.getName = function () {
  return `Hello ${this.name}`;
};

console.log(jessie.getName());

생성자함수 prototype에 있던 getName 함수를 인스턴스에서 새로운 내용으로 덮어썼기 때문에
인스턴스에서 가장 가까운 곳에 정의된 자신의 getName을 호출하게된다.

이것은 교체된 것이 아니라 덮어쓰여진 것이기 때문에
오버라이딩 된 상황이라도 생성자함수 prototype에 있던 getName함수를 다시 불러와
사용할 수도 있는 구조가 된다.


프로토타입 체인

위에서 만든 Person 생성자함수로 만든 jessie 인스턴스에서는 hasOwnProperty 함수를 실행할 수 있다.

1
console.log(jessie.hasOwnProperty("name")); // true

hasOwnProperty 함수는 Object.prototype에 정의된 메서드이다.
이것은 인스턴스가 Person.prototype 뿐만 아니라, Object.prototype도 상속받았다는 것을 의미한다.

자바스크립트는 객체에 원하는 프로퍼티가 없다면 [[Prototype]] 내부 슬록의 참조를 따라
부모의 프로토타입을 순차적으로 검색한다. 이것을 프로토타입 체인이라고 한다.

프로토타입 체인의 최상위 객체는 언제나 Object.prototype이다. 따라서 모든 객체는 Object.prototype을 상속받는다.


생성자함수 내부에 직접 정의 vs prototype에 정의

1
2
3
4
5
6
7
8
9
10
11
12
13
const Parent = function () {
  this.a = 1;
};
const child = new Parent();

Parent.prototype.b = 2;

console.log(child.a);
// child에는 a속성이 있다. 1 출력
console.log(child.b);
// child에는 b속성이 없다. prototype을 확인해보자. 있으므로 2 출력
console.log(child.c);
// child에는 c속성이 없다. prototype에도 없다. 최상위까지 확인해도 없으므로 undetined 출력

기본적으로 값을 참조하려고 할 때 체인을 따라 상위로 올라가면서 확인한다.
위 코드에서 확인한 child.b의 경우
__proto__를 생략해서 그렇지 인스턴스에 있는 값을 직접 가져온 것은 아니다.


Leave a comment