본문 바로가기

[OOP] JavaScript 객체 지향 프로그래밍 - 16. 생성자 함수를 통한 상속

 

 

들어가며

어느 정도 개발에 대한 감을 익혔다고 생각했습니다. 코드를 순서에 맞게 작성하는 능력들을 아주 조금이지만 쌓아 올릴 수 있었습니다. 하지만 시간이 지나면서 제 코드를 봤을 때 너무 더럽다고 느껴졌습니다. 중복되는 코드가 많았고, 비효율적인 코드들도 많이 볼 수 있었습니다. 코드를 조금 더 재사용성이 높게 작성한다면 더 효율적으로 시스템이 동작할 수 있었을 텐데, 아쉬움이 남았습니다. 이런 고민을 하면서 객체지향 프로그래밍에 대해 배워야겠다고 생각했습니다. 앞으로 객체지향을 배우면서 공부하고 느낀 점들을 정리해나가고자 합니다. 아래 내용은 생활코딩의 OOP 수업을 듣고 정리한 내용입니다.

 

 

 

 

 

 

 

 

 

 


 

 

 

 

 

 

 

 

 

 

 

생성자 함수를 통한 상속 : 소개

자바스크립트는 상속이 객체와 객체가 직접 상속하는 방법이 있고, constructor function을 통해서도 상속을 할 수 있습니다. class를 이용해서 상속하는 것이 훨씬 더 쉽습니만, class가 등장하기 전부터 사용할 수 있었던 prototype을 통해서 상속하는 방법을 소개할 것입니다. 이 과정에서 프로토타입은 중요한 주제입니다. 프로토타입에 대해 생각해보는 기회가 되면 좋겠습니다. 

 

 

먼저 전에 작성했던 Class를 활용한 코드를 살펴보겠습니다. 먼저, Person 클래스를 만들고, new를 통해 클래스가 생성될 때, constructor가 실행되면서 생성하려고 하는 객체의 초기값이 셋팅됩니다. 그 객체는 sum이라는 메서드를 갖고 있고, 그것은 객체의 소속이 아니고, 객체의 프로토타입의 소속이기 때문에 Person을 이용해서 생성되는 모든 객체가 공유하는 함수입니다. 

 

그리고 PersonPlus라는 클래스도 존재하는데, 여기에는 클래스의 모든 기능을 구현하는게 아니라 extends를 활용해서 Person의 기능을 상속 받습니다. 만약 부모 클래스가 갖고 있는 constructor를 실행하려면, super라는 메서드를 통해 실행할 수 있고, 나머지는 자식 클래스에서 별도로 실행하는 것을 통해 부모의 코드를 재활용하면서 자신만의 작업을 할 수 있습니다.

 

sum이라는 메서드 역시 부모가 갖고 있기 때문에 super.sum을 통해 재활용하고 그곳에 자식만의 작업을 추가합니다. 그리고 avg 메서드는 부모에게 없었던 메서드이기 때문에 PersonPlus에 추가된 메서드 입니다. 

 

지금까지 살펴봤던 클래스에 대한 부분입니다. 그렇다면, 이를 constructor function을 활용해서 객체를 생성하는 코드로 수정해보겠습니다.

 

 

 

 

 

 

 

 

 


 

 

 

 

 

 

 

 

 

 

 

 

 

생성자 함수를 통한 상속 : 부모 생성자 실행

새로운 파일을 만들어봅시다. 클래스를 왼쪽에, constructor를 통해 객체를 생성하는 로직은 오른쪽에 적어보겠습니다. 

 

 

코드를 보면, 아직 완벽하게 오른쪽에 코드를 작성한 것이 아닙니다. 몇 가지 해야할 것들이 있습니다. 먼저 PersonPlus라는 생성자를 호출할 때, 부모 클래스의 Person과 중복되지 않는 코드를 작성하기 위해서는 어떻게 해야할까요? 이때 해야할 것은 부모 함수를 호출하는 일입니다. 그러면, 부모 함수를 호출해서, 함수가 갖고 있는 기능들을 사용하고 싶다면 어떻게 해야할까요?

 

 

 

 

결론은 this에 집중하면 됩니다. Person 함수를 호출하고, 여기서 call 메서드를 사용하면 됩니다. 첫번째 인자로 PersonPlus라는 생성자가 new를 통해 호출될 때 만들어지는 객체는 this를 인자로 주면 됩니다. call 메서드에 의해 Person의 코드가 실행되는데, this는 생성하려고 하는 객체이기 때문에, 생성자를 통과시키면 원하는 일을 할 수 있습니다. 그리고 추가적으로 필요한 Personplus의 작업만 해주면 됩니다. 그럼 이는 Class에서 활용하는 super와 같은 일을 하고 있습니다.

 

여기서 중요한건, PersonPlus라는 함수는 sum 메서드를 아직 갖고 있지 않습니다. PersonPlus가 sum 메서드를 가지려면 어떻게 해야할까요?

 

 

 

 

 

 

 

 


 

 

 

 

 

 

 

 

 

 

 

생성자 함수를 통한 상속 : 부모와 연결하기

부모 constructor 함수를 콜할 때, This 객체를 살펴봤었습니다. 아직 우리 부모 함수와 자식 함수는 아무런 관련이 없습니다. 호출한 것 뿐입니다. 지금 상태로는 kim이라는 객체의 sum이라는 메서드를 호출할 때, PersonPlus는 sum이라는 메서드를 갖고 있지 않기 때문에, 실행할 수 없습니다. 

 

현재 우리는 이런 상태입니다.

 

 

출처 : 생활코딩 'JavaScript 객체 지향 프로그래밍 - 16.3. 생성자 함수를 통한 상속 : 부모와 연결하기'

 

 

부모 construct 함수인 Person이 있고, Person은 prototype object를 갖고 있습니다. 자식 construct 함수인 PersonPlus도 자신의 prototype 객체를 갖고 있습니다. 

 

이 상태에서 PersonPlus를 기반으로해서 new 연산자를 이용해서 kim이라는 객체를 만들었는데, 그러면 객체를 생성하면 어떤 일이 벌어질까요?

 

 

출처 : 생활코딩 'JavaScript 객체 지향 프로그래밍 - 16.3. 생성자 함수를 통한 상속 : 부모와 연결하기'

 

객체의 __proto__라는 프로퍼티가 자신을 생성한 생성자 함수의 프로토타입이 가리키고 있는 객체를 가리키게 됩니다. 이 상태에서 kim.avg라는 메서드를 호출하면, kim이라는 객체엔 avg 프로퍼티가 없으니 __proto__를 따라서 PersonPlus's prototype 객체의 avg가 있는지를 확인합니다. 있으면 실행합니다.

 

 

만약 kim.sum을 하면, sum 프로퍼티가 없으면 마찬가지로 __proto__를 따라서 PersonPlus's prototype을 살펴봅니다. 여기에도 없다면, 이 상태로는 kim이라는 객체는 sum이라는 메서드가 없다고 에러를 냅니다. 그럼 우리는 PersonPlus에서 sum이라는 메서드를 찾을 수 없을 때, Person이라는 construct 함수 프로토타입 객체에 있는 sum이라는 메서드를 호출하도록 하고 싶으면 어떻게 해야할까요?

 

 

출처 : 생활코딩 'JavaScript 객체 지향 프로그래밍 - 16.3. 생성자 함수를 통한 상속 : 부모와 연결하기'

 

 

PersonPlus's prototype에 있는 __proto__가 Person's prototype을 가리키게 하면 될 것입니다. 코드는 정말 쉽습니다. 

 

 

 

 

이렇게 PersonPlus.prototype.__proto__를 Person의 prototype에 연결하면 됩니다. __proto__를 대체해서 다른 코드를 작성한다면, Object.create(Person.prototype); 을 줍니다.

 

 

Object.create(Person.prototype)를 하면, Person.prototype 객체를 __proto__로 하는 새로운 객체가 됩니다. 그리고 우리가 여기에서 PersonPlus.prototype 를 이렇게 할당하면, 새로운 객체는 __proto__가 Person을 가리키기 때문에 잘 동작하게 됩니다.

 

 

 

여기서, console.log에서 kim의 constructor를 출력해보면, Person이라는 함수가 나옵니다. 그 이야기는 kim이 Person의 construct 함수입니다. 그런데 우리는 이게 아닙니다. kim의 construct 함수는 PersonPlus입니다. 이렇게 나오려면 어떻게 해야할까요?

 

 

결과적으로 이런 코드가 나옵니다. 왜 PersonPlus.prototype.constructor = PersonPlus를 추가해야 하는지 다음 시간에 살펴보겠습니다.

 

 

 

 

 

 

 

 

 


 

 

 

 

 

 

 

 

 

 

 

 

생성자 함수를 통한 상속 : constructor 속성은 무엇인가?

먼저 constructor라는 프로퍼티를 좀 살펴보겠습니다.

 

 

출처 : 생활코딩 'JavaScript 객체 지향 프로그래밍 - 16.4. 생성자 함수를 통한 상속 : constructor 속성은 무엇인가?'

 

 

먼저 Person 객체가 있다면, 객체가 프로토타입을 통해 Person's prototype 객체를 참조하고, Person's prototype 객체는 constructor를 통해 Person을 참고합니다. 즉 서로가 상호참조하고 있는 상태입니다. 그리고 우리가 new를 통해 Person의 새로운 객체를 만들면, 새로운 객체는 __proto__를 통해 constructor function의 프로토타입 객체를 가리킵니다. 그러면 여기서 kim.constructor가 무엇인지 묻는다면, 이를 어떻게 찾을 수 있을까요?

 

먼저 kim에는 constructor라는 프로퍼티가 없으니, 화살표를 따라 Person's prototype에서 찾아봅니다. 여기서도 없으면, constructor를 따라서 Person을 찾아봅니다. 이런 흐름을 따라보면, 객체가 어떤 공장 출신인지 파악할 수 있습니다. 

 

 

 

 

 

 

 

 

 


 

 

 

 

 

 

 

 

 

 

생성자 함수를 통한 상속 : constructor 속성 바로잡기

전에 만들었던 코드를 먼저 살펴보겠습니다.

 

 

 

Object.create를 했는데, 이를 하면 위에 있는 __proto__와는 다르게 새로운 객체로 PersonPlus의 프로토타입을 대체합니다. 그리고 PersonPlus의 프로토타입은 PersonPlus를 가리켰는데, 그것을 replace시켜서, 더이상 PersonPlus의 프로토타입은 더이상 PersonPlus를 가리키지 않습니다. 지금은 Person을 가리킵니다. 

 

 

만약 kim.constructor를 콘솔에 출력해보면, 아래와 같이 나옵니다. 

 

function Person(name, first, second) {
	this.name = name;
    this.first = first;
    this.second = second;
}

 

즉, Object.create(Person.prototype)을 통해 만들어진 객체는 Person 프로퍼티를 프로토타입으로 하는 객체입니다. 만약 kim의 constructor를 출력했을 때, Person이 아니라, PersonPlus가 나오게 하고 싶다면 어떻게 해야할까요? 

 

 

이렇게 한다면,  personPlus.prototype.constructor를 PersonPlus로 지정한다면 아래와 같이 출력될 것입니다. 

 

function PersonPlus(name, first, second, third) {
	Person.call(this, name, first, second);
    this.third = third;
}

 

그런데도 이 것이 계속 문제가 됩니다. 만약 여기서 아래의 코드를 위로 올려버리면 어떻게 될까요?

 

 

 

 

 

그러면 Object.create가 기존에 있었던 avg 함수를 갖고 있는 프로토타입을 replace하기 때문에, avg기능이 지워집니다. 그런데 이 방식을 사용하지 않고, __proto__만 했다면, 사라지지 않습니다. __proto__ 방식은 기존에 있었던 객체를 replace하지 않고, __proto__만 바꾸기 때문에, 한방에 해결될 순 있지만, __proto__는 비표준이기 때문에, __proto__는 잘 선택해서 사용해봐야합니다. 

 

함수 간의 객체를 쓰는 것보다, class를 사용해서 객체를 사용하는게 훨씬 깔끔해보입니다. 한편으로 자바스크립트를 정복하는데는 프로토타입과 __proto__의 미묘한 관계를 알면 좋습니다.

 

 

 

 

 

 

 

 

 


 

 

 

 

 

 

 

 

 

마치며

객체지향에 대한 기본을 쌓아가고 있습니다. 앞으로 포기하지 않고, 하나씩 지식을 쌓아 가다 보면 언젠가는 기초가 튼튼한 개발자가 될 수 있을 것이라 믿습니다. 제대로 된 방향으로 하나씩 공부하고 싶습니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 

 

 

 

 

 

 

 

 

 

 

 

출처