본문 바로가기

[JS] 객체 지향 프로그래밍 - 클래스와 인스턴스

[JS] 객체 지향 프로그래밍 - 클래스와 인스턴스

객체 지향 프로그래밍(OOP, Object-oriented programming)

 

객체 지향 프로그래밍(OOP, Object-oriented programming)은 사람이 세계를 보고 이해하는 방법과 매우 흡사하다. 코드를 추상화하여 직관적으로 생각할 수 있기 때문에, 이미 오래전부터 프로그래밍 방법론으로 매우 빠르게 적용되었다. 객체 지향 프로그래밍을 철저하게 적용한 프로그래밍 언어 Java나 C#과는 다르게, JavaScript에서 OOP를 구현하는 방법은 조금 독특하다.  

 

상황에 맞는 방법론을 선택하면 된다.

 

객체 지향 프로그래밍은, 절차 지향 프로그래밍과는 다르게 데이터와 기능을 한곳에 묶어서 처리한다. 속성과 메서드가 하나의 "객체"라는 개념에 포함되며, 이는 JavaScript 내장 타입인 object(이하, object literal)와는 다르게, 클래스(Class)라는 이름으로 부른다. 

 


 

메서드 호출 

메서드 호출은 객체.메서드() 과 같이 객체 내에 메서드를 호출하는 방법을 의미한다.

 

⚠️ 메서드 호출 방식을 이용할 때에는 화살표 함수를 쓰지 않는다. (mdn 화살표 함수 설명)

let counter1 = {
  value: 0,
  increase: function() {
    this.value++ // 메서드 호출을 할 경우, this는 counter1을 가리킨다.
  },
  decrease: function() {
    this.value--
  },
  getValue: function() {
    return this.value
  }
}

counter1.increase()
counter1.increase()
counter1.increase()
counter1.decrease()
counter1.getValue() // 2

 

 

클로저 모듈 패턴을 이용해 매번 새로운 객체 생성하기

위의 함수 counter1은 단 하나의 객체만 만들 수 있다. 만약 똑같은 기능을 하는 카운터가 여러 개가 필요하다면, 같은 코드를 그대로 복사/붙여넣기 해야 하므로, 재사용성이 떨어진다.

 

똑같은 기능을 하는 카운터를 여러 개 만드는 방법 중 하나는, 아래와 같이 클로저 모듈 패턴을 이용할 수 있다. 

function makeCounter() {
  let value = 0;
  return {
    increase: function() {
      value++;
    },
    decrease: function() {
      value--;
    },
    getValue: function() {
      return value;
    }
  }
}

let counter1 = makeCounter()
counter1.increase()
counter1.getValue() // 1

let counter2 = makeCounter()
counter2.decrease()
counter2.decrease()
counter2.getValue() // -2

 

 

메소드에 화살표 함수를 쓸 수 없다

이야기 했듯이, 화살표 함수 표현은 메소드 함수가 아닌 형태로 사용 할 수 있다. 메소드로 사용하려고 한다면 다음과 같은 일이 발생한다.

 

'use strict';

var obj = { // does not create a new scope
  i: 10,
  b: () => console.log(this.i, this),
  c: function() {
    console.log( this.i, this)
  }
}
obj.b(); // prints undefined, Window {...} (or the global object)
obj.c(); // prints 10, Object {...}
 

 

화살표 함수는 자신의 this를 가지고("bind" 바인드)있지 않다. Object.defineProperty()

'use strict';

var obj = {
  a: 10
};

Object.defineProperty(obj, 'b', {
  get: () => {
    console.log(this.a, typeof this.a, this); // undefined 'undefined' Window {...} (or the global object)
    return this.a + 10; // represents global object 'Window', therefore 'this.a' returns 'undefined'
  }
});

클래스와 인스턴스

 

 

객체 지향 프로그래밍은 하나의 모델이 되는 청사진(blueprint)을 만들고, 그 청사진을 바탕으로 한 객체를 만드는 프로그래밍 패턴이다.

 

사실, 이미 자바스크립트에는 "객체"라는 개념이 객체 지향 프로그래밍과 무관하게 이미 존재한다. 따라서 여기서는 용어를 잘 구분하는 것이 중요하다.

 

  • 클래스(class) : 청사진
  • 인스턴스(instance) : 객체가 아닌 "청사진"을 바탕으로 한 객체로 인스턴스 객체(instance object)라고 부름

 

 

클래스를 만드는 방법 

 

클래스를 만드는 새로운 문법이 ES6(ECMAScript 6, 비교적 최근 자바스크립트 문법)에 도입되었다. 바로 class 키워드이다. (최근에는 ES6 방법을 주로 사용)

 

    ㄴ 생성자 함수 

 

 

 

여기서 보이는 함수는, 객체지향 프로그래밍에서 생성자(constructor) 함수라고 부르며 

인스턴스가 만들어질 때 실행되는 코드이다.

 

참고로 생성자 함수는 return 값을 만들지 않는다.

 

 

 

 

 

생성자 함수는 인스턴스 객체를 생성하고 초기화하는 특별한 메서드이다. (클래스 내에서 생성자 함수는 하나만 있을 수 있음)
생성자 함수를 작성하지 않으면, 기본 생성자(default constructor)가 제공되며,
기본(base) 클래스일 경우는 기본 생성자는 비어있으며, 파생(derived) 클래스일 경우 기본 생성자는 부모 생성자를 부른다.

 

    ㄴ 인스턴스를 만들 때

 

 

인스턴스를 만들 때에는 new 키워드를 사용한다.

 

즉시 생성자 함수가 실행되며, 변수에 클래스의 설계를 가진 새로운 객체, 즉 인스턴스가 할당된다.

 

각각의 인스턴스는 클래스의 고유한 속성과 메서드를 갖는다.

 

 

 

new 연산자 사용에도 화살표 함수를 쓸 수 없다

화살표 함수는 생성자로서 사용될 수 없으며 new와 함께 사용하면 오류가 발생한다.

var Foo = () => {};
var foo = new Foo(); // TypeError: Foo is not a constructor
 

this 키워드 

 

 

 

 

this는 인스턴스 객체를 의미한다.

 

parameter로 넘어온 브랜드, 이름, 색상 등은 인스턴스 생성 시 지정하는 값이며, 위와 같이 this에 할당한다는 것은 만들어진 인스턴스에 해당 브랜드, 이름, 색상을 부여하겠다는 의미이다.

 

 

 

 

메소드

 

 

- ES5는 prototype이라는 키워드를 사용해야 메서드를 정의할 수 있다. Car 클래스에 메서드를 추가하기 위해서는 `Car.prototype.refuel`과 같이 `prototype`을 이용해야 한다. 

 

- ES6에서는 생성자 함수와 함께 class 키워드 안쪽에 묶어서 정의한다.  

 


 

생성자 함수 정도만 객체 지향 프로그래밍에서 보편적인 개념이며 & 여기에 있는 용어는 오직 JavaScript에서만 유효한 용어다.

 


ES6 을 쓰자

ES5                                           vs                                            ES6

class Car {

    constructor(brand, name, color)

          this.brand = brand;

          this.name = name;

          this.color = color

          }

         drive() {   생략    }

}

 

let mini = new Car ('BMW', 'mini', 'black')

mini.color;

mini.drive();

 


 

배열도 사실 Array의 인스턴스다.

 

 

 

사실 배열은 Array의 인스턴스이다. 배열을 만들고 메서드를 쓰면서 이런 객체지향적인 방법을 이미 사용하고 있었던 것이다.

 

코드를 살펴보면 속성이나 메서드 사용법이 동일하다. 배열은 (대문자) Array의 인스턴스이다. 따라서 new Array(___, ___, ___) 와 같은 방식으로도 배열을 만들 수 있다.

 

대강 이런 느낌 이려나?

 

class Array {

    constructor(어쩌고저쩌고) 

          this. length = length; // 길이 어쩌고저쩌고 

          }

         push() {   생략    }

}

 

let arr = new Array ('BMW', 'mini', 'black')

arr = ['BMW', 'mini', 'black']

arr.length;

arr.push('IWant')

 

let theseDays = new Array ('sleep', 'eat', 'study', 'project')

anotherArr = ['sleep', 'eat', 'study', 'project']

arr.length;

arr.push('networking')

 


 

클래스의 원형 객체(prototype)

 

 

배열 메서드 (push, filter, forEach...) 등을 배울 때, mdn 문서를 보면 메서드들이 대부분 Array.prototype.메서드명 과 같이 안내되어 있다. 이는 모든 메서드들이 클래스의 원형 객체(prototype)에 정의되어 있기 때문이다.

 


위에서 본 클로저 모듈 패턴을 클래스 문법을 이용해 만들어보기

 

before
function makeCounter() {
  let value = 0;
  return {
    increase: function() {
      value++;
    },
    decrease: function() {
      value--;
    },
    getValue: function() {
      return value;
    }
  }
}

let counter1 = makeCounter()
counter1.increase()
counter1.getValue() // 1

let counter2 = makeCounter()
counter2.decrease()
counter2.decrease()
counter2.getValue() // -2

 

 

 

after
class Counter {
  constructor() {
    this.value = 0; // 생성자 호출을 할 경우, this는 new 키워드로 생성한 Counter의 인스턴스이다.
  }
  increase() {
    this.value++
  }
  decrease() {
    this.value--
  }
  getValue() {
    return this.value
  }
}

let counter1 = new Counter() // 생성자 호출
counter1.increase()
counter1.getValue() // 1

 

 

 

728x90

'FE > JavaScript' 카테고리의 다른 글

[JS] 프로토타입 체인  (0) 2023.03.15
[JS] 객체 지향 프로그래밍(OOP)의 4가지 주요 개념  (0) 2023.03.15
[JS] 추상화 **  (0) 2023.03.14
[JS] 내장 고차 함수 (filter, map, reduce)  (0) 2023.03.14
[JS] 고차함수  (0) 2023.03.14
⬆︎