강백호같은개발자

TIL(20/06/17) 객체 지향 프로그래밍 Object Oriented Programming(OOP) in JavaScript 본문

- 배움은 개발을 거들뿐(TIL)

TIL(20/06/17) 객체 지향 프로그래밍 Object Oriented Programming(OOP) in JavaScript

쨜리 2020. 6. 17. 16:54

자바스트립트의 객체 지향 프로그래밍 Object Oriented Programming(OOP)

 

객체 지향 프로그래밍이란?

JavaScript같은 high-level languages 에 있어서 지향하는 방향에 따라 2가지 종류로 나눠볼 수 있습니다.

하나는 절차 지향적 언어

다른 하나는 객체 지향적 언어

 

절차 지향적 언어는 함수나 변수가 나열되어 있고, 그것을
어떤 절차로 진행되는지 정하는 것이 매우 중요합니다. C언어나 HTML이 해당합니다.
객체 지향적 언어란 모든 것을 '객체' 또는 '물체'로 만드는 방식입니다. 
java, JavaScript, C++, python 등이 해당합니다.

 

이때 객체지향 프로그래밍이라고 하면, 일종의 철학입니다.

모든것을 object로 모든 설명 가능하다는 것입니다.

 

모든 것을 객체, object로 어떻게 설명할 수 있을까요.

모든 물체들은 속성과 매소드로 설명할 수 있습니다.

 

예를 들어 자동차 라는 객체를 생각해봅니다.

 

 

자동차의 속성으로는 색깔, 제조사, 브랜드명 등의 속성을 가지고 있습니다.

그리고 가속, 감속, 기어변속 등의 매소드를 가지고 있습니다. 

이것들은 모든 자동차들이 가지고 있는 속성과 매소드입니다. 

 

바로 이때 객체에 대한 데이터 형식과 사용 가능한 속성과 매소드에 대한 정의를 'class'라고 합니다.

그리고 객체는 클래스의 인스턴스가 됩니다. 

예를 들어서 위의 예시의 클래스에서 만들어지는 객체는 아반떼, K5, G70 등이 있습니다. 

아반떼와 K5와 G70은 각각 색깔과 제조사, 브랜드명이라는 속성을 가지고 있고,

가속, 감속, 기어변속 등의 매소드를 가지고 있습니다.

 

 

그렇다면 JavaScript에서 Object를 만드는 방법은 어떤 것들이 있을까요?

 

 

JavaScript에서 Object를 생성하는 여러가지 방법들

ES6의 `Class` 가 나오기 이전에 사용했던 Class 선언 방식을 살펴봅니다.
Class 는 하나의 정형화된 모델을 만들어두고, 그 모델의 복제품(인스턴스)를 만드는 방식으로 사용합니다.

바로 이때, 모델을 두고 찍어내는 기능을 함수를 이용할 수 있다는 점을 기억해야합니다.

ES5 방식

1. Functional Instantiation 방식 (함수 인스턴스 방식)

함수를 이용하여 찍어냅니다. 
함수 안에서 인스턴스를 선언해주고
또 속성과 메소드를 만들어서 함수 실행시 리턴 값으로 인스턴스를 받아봅니다.

// 우선 Car 라는 함수를 함수 표현식으로 만들어봤습니다.
var Car = function(){

};
// 그리고 함수를 실행했을 때, '찍어낼' 객체(인스턴스)를 선언해줍니다. 그리고 함수의 결과로 리턴합니다.
Var Car = function(){
  var someInstance = {};

  return someInstance;
};
// someInstance 객체의 position이라는 속성값을 0으로 초기화해줍니다. 
// 이제 Car 함수가 실행되어 나오는 인스턴스의 position 속성은 항상 0일 것입니다.
Var Car = function(){
  var someInstance = {};
  someInstance.position = 0;
  return someInstance;
};
// someInstance에 move라는 메소드를 추가합니다. 
// function 내부의 this는 매소드에서 사용되었기 때문에 someInstance 객체를 뜻하고
// this.position 은 someInstance.position과 의미가 같습니다.
// this.position을 실행하게 되면, position은 1만큼 증가할 것입니다.

Var Car = function(){
  var someInstance = {};
  someInstance.position = 0;
  someInstance.move = function(){
    this.position += 1;
  }
  return someInstance;
};
Var Car = function(){
  var someInstance = {};
  someInstance.position = 0;
  someInstance.move = function(){
    this.position += 1;
  }
  return someInstance;
};

var car1 = Car();
var car2 = Car();
car1.move();

// 이렇게 Car들을 찍어냅니다. car1.move()를 실행한 후 
// car1을 console.log로 찍어보면 position 이 1인 것을 확인할 수 있습니다.
Var Car = function(position){
  var someInstance = {};
  someInstance.position = position;
  someInstance.move = function(){
    this.position += 1;
  }
  return someInstance;
};

var car1 = Car(5);

//물론 이렇게 position의 초기값을 지정해줄 수도 있습니다. 

 

 

2. Functional Shared (함수 공유 방식)

이 방식은 Car라는 객체 변수를 선언하는 함수 하나와, 
활용될 메소드를 만들고, 이 메소드를 Car 함수안의 메소드로 설정하는 함수 하나로 구성되어 있습니다.
왜 번거롭게 이렇게 만들었을까요?


이전 functional 방식에서는 인스턴스를 생성할 때마다
모든 메소드를 인스턴스 객체인 someInstance에게 할당하기 때문에 
인스턴스들의 메모리가 더 나가게됩니다.

 

하지만, Functional Shared  방식은

somemethods라는 객체에 있는 메소드의 메모리 주소만 가지고,

참조하는 방식이기 때문에 

인스턴스의 메모리가 늘지 않습니다. 
메모리 효율이 좋아지는 것입니다. 

// Car 함수를 함수표현식으로 만들었습니다. 
// position을 someInstance의 속성으로 넣어줬습니다.
// position 초기값을 설정할 수 있도록 인자로 받고 있구요.

var Car = function(position){
  var someInstance = {
    position: position, 
  };
  return someInstance;
}
// 그리고 메소드를 담아줄 객체도 생성합니다. 모든 메소드는 someMethods에 담을 것입니다.
// 이 예제에서는 move함수 하나만 있습니다.
// 함수 외부에 또다른 객체와 그 객체 안의 move 매소드를 만드는 것이 흥미롭습니다.

var someMethods = {};
someMethods.move = function(){
  this.position += 1;
}

var Car = function(position){
  var someInstance = {
    position: position, 
  };
  return someInstance;
}
// 그리고 someInstance와 someMethods를 합치는 extend 함수를 만들어서 
// Car 함수 내부에서 합쳐줍니다.
// extend함수는 someMethods 객체를 탐색하면서 있는 move메소드를 
// 객체인 someInstance의 메소드로 넣어줍니다.

var extend = function(){
  for(var key in from){
    to[key] = from[key];
  }
};

var someMethods = {};
someMethods.move = function(){
  this.position += 1;
}

var Car = function(position){
  var someInstance = {
    position: position, 
  };
  extend(someInstance, someMethods);

  return someInstance;
}
var extend = function(){
  for(var key in from){
    to[key] = from[key];
  }
};

var someMethods = {};
someMethods.move = function(){
  this.position += 1;
}

var Car = function(position){
  var someInstance = {
    position: position, 
  };
  extend(someInstance, someMethods);

  return someInstance;
}

// 이제 차를 찍어낼 수 있습니다.
var car1 = Car(5);
var car2 = Car(10);

 

3. Prototypal Instantiation 방식(프로토타입 인스턴스화 방식)

이 방식은 인스턴스 객체를 리턴하는 Car 함수와 move 매소드를 가진 객체 someMethods가 있을 때,

Object.create(); 를 통해서 매소드를 인스턴스 객체의 프로토타입으로 지정하는 방식입니다. 
소괄호에 프로토타입으로 들어갈 객체를 지정해주면 됩니다.
그렇게하면 새로 만들어지는 인스턴스 객체의 프로토타입으로 move를 가지게 됩니다.

// Functional Shared 방식처럼, 인스턴스 객체를 리턴하는 Car 함수와
// move 매소드를 가진 someMethods 객체를 선언해줍니다.

var someMethods = {};
someMethods.move = function(){
  this.position += 1;
};

var Car = function(){
  var someInstance = {};
  someInstance.position = position;
  return someInstance;
};
// 그리고 처음 인스턴스인 객체를 선언해줄 때 빈객체가 아니라
// Object.create(someMethods);를 붙여줍니다.
// 그리고 인스턴스들을 찍어냅니다.

var someMethods = {};
someMethods.move = function(){
  this.position += 1;
};

var Car = function(position){
  var someInstance = Object.create(someMethods);
  someInstance.position = position;
  return someInstance;
};

var car1 = Car(5);
var car2 = Car(10);

Object.create는 특정 객체를 프로토타입으로 하는 객체를 생성해주는 함수입니다. 

MDN의 설명입니다. 
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/create

 

이 방식도 참조를 활용한다고 생각하면 메모리를 적게 사용할 것 같습니다. 

 

 

4. Pseudoclassical 방식(유사classical 방식)

이 방식을 가장 많이 이용합니다. 
초기값으로 인자를 할당받는 position이라는 속성을 가지는 함수 Car를 만든 다음,
move라는 메소드를 Car의 프로토타입으로 지정하는 방식입니다. 

// position을 this.position에 할당하는 함수를 만듭니다.
var Car = function(position){
  this.position = postion;
};
//그리고 move라는 메소드를 만들어서 Car의 프로토타입으로 만듭니다.

var Car = function(position){
  this.position = postion;
};

Car.prototype.move = function(){
  this.position += 1;
};
var Car = function(position){
  this.position = position;
};

Car.prototype.move = function(){
  this.position += 1;
};

var car1 = new Car(5);
var car2 = new Car(10);

// 끝입니다. 대신 이렇게 프로토타입으로 찍어내려면 
// new operator를 붙여야합니다.

이렇게 간단해질 수 있나요?
근데 Car 함수가 객체를 가진다는 정의가 사라져도 괜찮나요?
제 생각에는 prototype 이라는 키워드 자체가, 원형객체에 해당하는 특성을 의미하기 때문에
이미 Car를 원형 객체로 선언해준 것이나 다름 없기 때문에 
Car는 객체가 되는 것으로 이해했습니다.

 

ES6 방식

ES6 에서는 class 라는 키워드와 constructor를 사용하여 정의합니다.
constructor에서는 속성을 정의하고 class 안에서 메소드를 정의합니다.

class Car(){
  constructor(position){
  	this.position = position;
  }
  
  move(){
    this.position += 1;
  }
}

var car1 = new Car(5);
var car2 = new Car(10);

 

 

JavaScript에서 Prototype은 무엇이고 왜 사용해야 하는가?

prototype 이란 원형 객체를 의미합니다. 
그러니까 class에서 매소드를 정의할 때 ES5 문법에서 Pseudoclassical 방식을 생각해보면
Car 라는 class가 원형 객체인 것이고, 

이 원형객체에 move라는 메소드를 정의하는 것이죠.

var Car = function(position){
  this.position = position;
};

Car.prototype.move = function(){
  this.position += 1;
};

var car1 = new Car(5);

그리고 각 인스턴스를 생성할때 원형객체의 매소드를 활용할 수 있는 것입니다.
그러고보면 JavaScript의 모든 것은 객체지향적으로 이루어져있기 때문에

배열 역시도 Array 라고하는 원형객체에 정의되어 있는 prototype.메소드 들을 

이용할 수 있는 것입니다. (forEach 라든지 push 등.)

 

 

 

 

 

자바스크립트의 객체 지향 프로그래밍 Object Oriented Programming(OOP) 에 대해서 알아보았습니다.

Comments