[Begachu]JavaScript에서의 객체지향 프로그래밍

ARGOS JavaScript·2021년 10월 24일
0

이번주에는 Javascript에서의 객체지향 프로그래밍에 대해 공부해보고자 합니다.


클래스 기반 vs 프로토타입 기반 언어

객체지향 언어는 크게 클래스 기반 언어프로토타입 기반 언어가 존재합니다.

클래스 기반 언어는 클래스를 이용해서 객체의 형태와 기능을 정의하고, 생성자를 통해 인스턴스를 만들어 사용합니다. 클래스 기반 언어의 예로는 Java, C++가 있습니다.
클래스 기반 언어는 모든 인스턴스가 클래스에 정의된 것과 같은 구조를 가지며, 이는 런타임에 바꿀 수 없습니다.
반면 프로토타입 기반 언어는 클래스 대신 프로토타입 객체를 이용하기 때문에 객체의 자료구조, 메서드 들을 동적으로 바꿀 수 있습니다.

클래스 기반 언어는 정확성, 안전성, 예측성 등의 관점에서 장점을 지니고, 프로토타입 기반 언어는 동적으로 객체의 구조와 동작 방식을 바꿀 수 있다는 장점을 지닙니다.

Javascript는 프로토타입 기반 언어로, 이번 시간에는 Javascript에서 프로토타입 객체를 이용한 객체지향 프로그래밍 방법에 대해 알아볼 것입니다.


클래스, 생성자, 메서드

Class 키워드를 직접 제공하는 클래스 기반 언어와 달리, Javascript에서는 class와 같은 개념이 존재하지 않습니다. 하지만 우리는 Javascript의 함수를 이용하여 클래스와 함께 생성자, 메서드도 모두 구현할 수 있습니다.

function Person(arg){
    this.name = arg;
    
    this.getName = function() {
        return this.name;
    }
}

var me = new Person(“begachu”);
console.log(me.getName());    // 출력 : begachu

me.setName(“yugyoung”);
console.log(me.getName());    // 출력 : yugyoung

위의 코드는 이전 시간에 공부했던 생성자 함수 방식으로 객체를 생성하여 사용한 경우입니다. 하지만 이 예제는 문제가 있을 수 있습니다. 위의 코드의 Person 생성자 함수를 이용해 여러 개의 객체를 만든다고 가정해봅시다.

var me = new Person(“me”);
var you = new Person(“you”);
var him = new Person(“him”);

위의 코드로 만들어진 세 객체는 정상적으로 작동할 것입니다. 하지만 이 경우 각 객체는 setName()과 getName()이라는 동일한 일을 수행하는 함수들을 각각의 메모리에 올려놓고 사용하고 있습니다. 이는 자원낭비를 가져옵니다.

그렇다면 이러한 자원낭비를 막기 위해서는 어떻게 해야할까요?

function Person(arg) {
    this.name = arg;
}

Person.prototype.getName = function() {
    return this.name;
}

Person.prototype.setName = function(value) {
    this.name = value;
}

var me = new Person(“me”);
var you = new Person(“you”);
console.log(me.getName());
console.log(you.getName());

위와 같이 공통적인 프로퍼티를 Person 함수 객체의 prototype 프로퍼티로 정의하면 됩니다.
더 나아가 아래의 메서드를 정의해 사용할 수도 있습니다.

Function.prototype.method = function(name, func) {
    if (!this.prototype[name])
        this.prototype[name] = func;
    }
}

더글라스 크락포드는 함수를 생성자로 사용해 프로그래밍하는 것을 추천하지 않습니다. 이는 생성자 함수가 new를 이용한 호출 뿐만 아니라 직접 호출도 가능하기 때문입니다. 또한 호출 방법에 따라 this에 바인딩되는 객체가 달라진다는 점도 그 이유입니다. 따라서 크락포드는 일단은 생성자로 사용되는 함수의 첫 글자를 대문자로 표기할 것을 권고하고 있습니다.

상속

Javascript에서는 클래스를 기반으로 하는 전통적인 상속을 지원하지 않습니다. 대신 객체 프로토타입 체인을 통해 상속을 구현할 수 있습니다. 이 방식은 크게 두 가지로 나뉩니다.

  1. 클래스 기반 상속 방식 모방
  2. 클래스 없이 객체의 프로토타입으로 상속 구현

특히 2번째 방법은 프로토타입을 이용한 상속이라고 합니다. 이번 시간에는 두 방법 모두에 대해 알아보고자 합니다.

프로토타입을 이용한 상속

아래의 코드를 볼까요?

function create_object(o) {
  function F() {}
  F.prototype = o;
  return new F();
}

코드에서 create_object() 함수는 인자로 들어온 객체를 부모로 하는 자식 객체를 생성해 이를 반환하고 있습니다. 이렇게 반환된 객체는 인자로 들어온 객체 o의 프로퍼티에 접근할 수 있으며, 또한 자신의 프로퍼티도 만들 수 있습니다.

이처럼 프로토타입의 특성을 활용해 상속을 구현하는 것이 프로토타입 기반의 상속입니다.

프로토타입 기반의 상속은 클래스에 해당하는 생성자 함수, 인스턴스를 따로 생성하지 않고 부모 객체를 프로토타입 체인으로 참조하여 상속을 구현한다는 특징이 있습니다.

자식 객체에서의 overriding

앞에서는 자식 객체가 부모 객체의 함수를 그대로 상속받는 경우만 다루었습니다. 이번에는 자식 객체가 부모의 함수를 오버라이딩하는 방법에 대해서 알아보고자 합니다.
이를 위해서 이번에는 JQuery의 extend() 함수를 사용해보고자 합니다.
extend() 함수는 다음과 같이 구현되어 있습니다.

jQuery.extend = jQuery.fn.extend = function(obj, prop) {
  if (!prop) { prop = obj; obj = this; }
  for ( var i in prop ) obj[i] = prop[i];
  return obj;
};

위의 코드를 간단히 분석하자면, 인자가 하나만 들어오는 경우는 현재 객체의 인자로 들어오는 객체의 프로퍼티를 복사하고, 두 개가 들어오는 경우 첫 번째 객체에 두 번째 객체의 프로퍼티를 복사한다는 것입니다.

하지만 이 경우 obj[i] = prop[i]는 얕은 복사이므로 두 번째 객체의 프로퍼티가 변경되면 첫 번째 객체의 프로퍼티도 같이 변경되게 됩니다.
따라서 extend 함수를 구현하는 경우 대상이 객체일 때 깊은 복사를 하는 것이 일반적입니다.

사용예시는 다음과 같습니다.

var person = {
  name = "test",
  getName : function() {
    return this.name;
  },
  setName : function(arg) {
    this.name = arg;
  }
};

function create_object(o) {
  function F() {};
  F.prototype = o;
  return new F();
}

function extend(obj,prop) {
  if(!prop) { prop = obj; obj = this; }
  for ( var i in prop ) obj[i] = prop[i];
  return obj;
}

var student = create_object(person);
var added = {
  setAge : function(age) {
    this.age = age;
  },
  getAge : function() {
    return this.age;
  }
};

extend(student, added);

student.setAge(23);
console.log(student.getAge());

클래스 기반의 상속

클래스 기반의 상속도 사실은 프로토타입을 이용한 상속과 유사하게 함수의 프로토타입을 적절히 이용하여 상속을 구현합니다.
하지만 객체 리터럴로 생성된 객체의 상속이었던 프로토타입 이용한 상속과 달리, 클래스 기반의 상속에서는 클래스 역할을 하는 함수로 상속을 구현합니다.

profile
ARGOS JavaScript 정복하기

0개의 댓글