본문 바로가기
Language/Javascript

OOP (3) Javascript에서의 객체지향 프로그래밍 (ES6 이전의 Pseudoclassical한 방법)

by joooing 2021. 1. 16.
반응형

ES6 이전에는 프로토타입기반 언어인 Javascript에서 Prototype을 통해 객체를 생성하고자 했던 다양한 시도와 방법들이 있었다. 먼저 대표적인 방식인 pseudoclassical한 방식에 대해 살펴보며 클래스 없는 객체 지향을 위해 어떤 방식으로 시도를 했는지 알아보려고 한다. 그리고 나서 ES6 이후 등장한 class 키워드를 이용해 보다 쉽게 객체 지향을 구현하는 방법에 대해 이야기해보려 한다.

 

before

이 글을 보기 전 알아야 하는 개념들이다. 모두 안다면 바로 글을 봐도되고 모르는 개념이 있다면 이전 글들을 보고 오는 것을 추천한다.

 

prototype = 원형이 되는 객체 (속성 및 메서드 정의 가능)
constructor : 인스턴스 초기화 시 실행되는 생성자 함수
this : 함수 실행 시마다 생성되는 고유한 실행 컨텍스트  (new로 인스턴스를 생성한 경우, this = 인스턴스)

 

Pseudoclassical ver.(ES6 이전)


ES6 이전에 클래스 개념을 흉내내던 대표적인 방식이다. 지향하는 방법은 아니지만 이 방법을 먼저 익혀두면, Javascript에서 객체지향 프로그래밍을 구현하는 방법에 대한 전반적인 그림을 그릴 수 있게 되기 때문에 사용하지 않더라도 알아두는 것을 추천한다. 이 방법을 활용하고자 한다면 네 단계만 기억하자! (각 단계의 제목은 쉽게 기억하려고 붙여둔 것이기 때문에 각자 자기가 이해한대로 다시 이름을 지어도 된다)

 

var Human = function(name) {
    this.name = name;
}

var Student = function(name) {
    Human.call(this, name);    // 1. 자식&부모 연결하기
};

Student.prototype.learn = function() {};    // 2. 자식 기능 추가하기
Student.prototype = Object.create(Human.prototype); // 3. 다형성 구현하기
Student.prototype.constructor = Student;    // 4. 연결고리 끊기

 

1 단계. 자식 & 부모 연결하기

먼저 자식의 생성자 함수 안에서 call 이나 apply 메서드를 사용해 부모 생성자 함수와 연결해야 한다. 위에서는 call 을 사용한 방법만 소개했지만, 매개변수를 배열로 해서 apply를 사용할수도, 여러개의 인자를 arguments로 받을 수도 있다. 주의할 점은 call과 달리 apply의 인자로는 배열을 넣어야 한다는 것이다! 같은 기능을 하기 때문에 둘 중 무엇을 쓰던 상관 없지만, Spread 연산자(...) 등을 적절하게 사용함으로써 매개변수로 올바른 데이터 타입을 입력해주도록 주의하자!

 

Human.call(this, name);
Human.apply(this, arguments);
Human.call(this, ...arguments);

 

2 단계. 자식 기능 추가하기

prototype 속성은 원형이 되는 객체로, 속성 및 메서드 정의 가능하다고 했었다. 추가로 할당 가능한(assignable) 특징이 있어서 아래와같이 속성 뒤에 점 하나만 붙이면 다양한 속성이나 메서드를 얼마든지 추가할 수 있다. 아래 예시에서는 Student라는 자식 생성자 함수에 learn이라는 기능(메서드)을 추가해 주고 있다. 이처럼 생성자함수의 prototype 속성을 통해 기능을 손쉽게 추가할 수 있다.

 

Student.prototype.learn = function() { return 지식 };

 

3 단계. 다형성 구현하기

자식 생성자 함수의 prototype 객체를 부모 생성자 함수의 prototype 과 연결시켜주는 작업도 필요하다. 이 과정도 간단히 Object.create 메서드를 통해 구현할 수 있다.

 

Student.prototype = Object.create(Human.prototype);

 

Object.create()첫번째 인자로 들어가는 객체를 바탕으로 → 새로운 prototype객체를 생성하는 메서드이다. 복사해서 새로 만든다는 사실에 주의해야 한다. Human.prototype의 기능을 복사한 새로운 prototype 객체를 만드는 것이다! 굳이 Object.create 를 쓰는 이유가 있을까? 아래 두 코드의 차이점을 살펴보자.

 

Student.prototype = Human.prototype; 
Student.prototype = Object.create(Human.prototype); 

 

부모 생성자 함수의 prototype 을 바로 할당하면, 부모 생성자 함수의 주소값까지 그대로 가져와서 두 함수가 같은 값을 참조해 종속적인 관계가 되어버린다. (깊은 복사) Student 객체에 변화가 생기면, Human 객체도 같은 변화가 적용되는 것이다. 이렇게 자식과 부모가 항상 동일해진다면 굳이 상속을 할 필요도 없어질 것이다.

 

부모로부터 속성을 물려받지만, 자식들도 각자 자신만의 기능을 추가하고 싶다면 Object.create()를 사용해야 한다. 얕은 복사를 하는 Object.create()새로운 prototype 객체와 새로운 참조 주소값을 만들어내기 때문이다. 이로써 자식은 이제 부모와는 독립적으로 존재할 수 있게 된다.

 

 

4 단계. 연결고리 끊기

Object.create()를 통해 주소값을 바꿔준다고 해도, 확인해보면 constructor은 아직 부모객체로 지정되어 있을 것이다. 따라서  자식 생성자 함수의 constructor 를 자기 자신으로 재할당해주는 과정이 필요하다. 즉 자식은 자기 자신으로 constructor을 설정해야 한다.

 

Student.prototype.constructor = Student;

 

 

 

이로써 부모의 특징은 모두 물려받으면서도 자식은 본인만의 개성있는 기능을 마음껏 추가할 수 있게 되었다. 다음에는 ES6문법 이후로 등장한 class 표현식을 통해 훨씬 편하게 객체 지향을 구현하는 방법을 알아보도록 하겠다. 배경이 되는 접근 방식은 비슷하기 때문에 ES6 이전의 방법도 완벽히 익히고 가도록 하자!

반응형

댓글