OOPObject - oriented programming
은 사람이 세계를 보고 이해하는 방법과 매우 흡사하다. 코드를 추상화하여 직관적으로 생각할 수 있기 때문에, 이미 오래전부터 프로그래밍 방법론으로 빠르게 적용됐다.
객체 지향 프로그래밍을 철저하게 적용한 프로그래밍 언어 Java C#과는 다르게, 자바스크립트에서 OOP를 구현하는 방법은 조금 독특하다.
메서드 호출은 객체.메서드()
와 같이 객체 내에 메서드를 호출하는 방법을 의미한다.
단순 객체를 사용한 이러한 예제를 흔히 볼 수 있다.
//메서드 호출 방식을 이용할 때에는 화살표 함수를 쓰지 않는다.
let counter1 = {
value:0,
increase: function(){
this.value++ // 메서드를 호출 할 경우, this 는 counter1을 가르킨다.
},
decrease: function(){
this.value--
},
getValue: function(){
return this.value
}
}
counter1.increase()//+1
counter1.increase()//+1
counter1.increase()//+1
counter1.decrease()//-1
counter1.getValue()//=2
위의 counter1은 단 하나의 객체만 만들 수 있다. 만약 똑같은 기능을 하는 카운터가 여러개가 필요하다면, 이 코드를 여러번 만들어야 할까? 같은 코드를 그대로 복붙해야 하므로, 재사용성이 떨어진다.
똑같은 기능을 하는 카운터를 여러 개 만드는 방법 중 하나는, 아래 예제 코드와 같이 클로저 모듈 패턴을 이용할 수 있다.
function makeCounter(){
let value = 0;
return{
increase: function(){
value++;
},
decrease: function(){
value--;
},
getValue: function(){
return value;
}
}
}
let counter1 = makeCounter()
let counter2 = makeCounter()
객체 지향 프로그래밍의 기초가 도는 용어부터 설명한다.
객체 지향 프로그래밍은 하나의 모델이 되는 청사진을 만들고, 그 청사진을 바탕으로 한 객체를 만드는 프로그래밍 패턴이다.
앞서 언급된 청사진은 자동차 생산을 위한 설계도에 비유된다. 자동차가 기능하기 위해서는 네 바퀴와 핸들, 좌석 그리고 엔진이 필요할 것이다. 이러한 기본 설계는 차의 종류에 상관없이 대체적으로 동일하게 적용된다.
이런 설계도(청사진)을 바탕으로 각각의 객체가 특정한 자동차 모델로 나오게 되는 것이다.
사실, 이미 자바스크립트에는 "객체"라는 개념이 객체 지향 프로그래밍과 무관하게 이미 존재한다. 따라서 여기서는 용어를 잘 구분하는 것이 중요하다.
앞으로, 그냥 객체가 아닌 청사진을 바탕으로 한 객체는 인스턴스 객체(instance object), 줄여서 인스턴스라고 부르겠다. 청사진은 클래스라고 부른다.
객체를 어떤 식으로 만드는지 살펴보면, 그냥 일반적인 함수를 정의하듯 만든다.
이때 함수를 이용하는 방법이 조금 다르다. 그냥 실행하는 것이 아니고 new 키워드를 써서 만든다. 이는 새로운 인스턴스를 만든 방법이다.
일반적인 다른 함수와 구분하기 위해 클래스는 보통 대문자로 시작하며 일반 명사로 만든다.
일반적인 함수는 적정한 동사를 포함하고 소문자로 시작한다.
//ex)
function Car(color){~~~}
let avante = new Car('blue');
let mini = new Car('cyan');
let beetles = new Car('red);
그리고 클래스를 만드는 새로운 문법이 ES6에 도입되었다. 바로 class 키워드 이다.
//ES5 클래스는 함수로 정의할 수 있다.
funtion Car(brand, name, color){
//인스턴스가 만들어질 때 실행되는 코드
}
//ES6 에서는 class 라는 키워드를 이용해서 정의할 수도 있다.
class Car{
constructor(vrand, name, color){
//인스턴스가 만들어질 때 실행되는 코드
}
}
여기서 보이는 함수는, 객체지향 프로그래밍에서 생성자(constructor) 함수라고 브른다. 인스턴스가 만들어질 때 실행되는 코드.
참고로 생성자 함수는 return 값을 만들지 않는다.
인스턴스를 만들 때에는 new 키워드를 사용한다. 즉시 생성자 함수가 실행되며, 변수에 클래스의 설계를 가진 새로운 객체, 즉 인스턴스가 할당된다.
각각의 인스턴스는 클래스의 고유한 속성과 메서드를 갖게 된다.
let avante = new Car('hyundai', 'avante', 'black')
let mini = new Car('bmw','mini','white')
let beetles = new Car('volkswagen','beetles','red')
//각각의 인스턴스는 Car라는 클래스의 고유한 속성과, 메소드를 갖는다.
속성과 메서드란 용어가 낯설게 느껴진다? 현실 세계를 생각해 보면 조금 더 쉽게 와닿는다.
속성 | 메소드 |
---|---|
brand | refuel ( ) |
name | setSpeed ( ) |
color | drive |
currentFuel | |
maxSpeed |
예를 들어 자동차의 속성은, 브랜드, 차 이름, 색상, 현재 연료 상태, 최고 속력 등이 있을 수 있다.
메서드는 쉽게 말해 "객체에 딸린 함수"이다. 연료 주입, 속력 설정, 운전 등이 메서드 이다.
//ES5
function Car(brand, name,color){
this.brand = brand;
this.name = name;
this.color = color;
}
//ES6
class Car{
chonstructor(brand, name,color){
this.brand = brand;
this.name = name;
this.color = color;
}
}
this라는 새로운 키워드가 등장한다. 객체지향 프로그래밍에서는 빠지지 않고 등장하므로 간단하게 알아두자.
한마디로 this는 인스턴스 객체를 의미한다. parameter로 넘어온 브랜드, 이름, 색상 등은 인스턴스 생성 시 지정하는 값이며, 위와 같이 this에 할당한다는 것은 만들어진 인스턴스에 해당 브랜드, 이름, 생상을 부여하겠다는 의미이다.
//ES5
function Car(brand, name, color){/*생략*/}
Car.prototype.reful = function(){
//연료 공급을 구현하는 코드
}
Car.prototype.drive = function(){
//운전을 구현하는 코드
}
//ES6
class Car {
constructor(brand, name, color){/*생략*/}
refuel(){
}
drive(){
}
}
이번에는 메서드 정의이다.
ES5는 prototype이라는 키워드를 사용해야 메서드를 정의할 수 있다. Car 클래스에 메서드를 추가하기 위해서는 Car.prototype.refuel
과 같이 prototype
을 이용해야 한다. 이후 prototype
에 대해서 자세히 공부하고, 지금은 정의 시 사용법만 숙지하자.
ES6에서는 생성자 함수와 함께 class 키워드 안쪽에 묶어서 정의힌다.
refuel(){}
,drive(){}
와 같이 작성되어 있는 부분이다.
let avante = new Car('hyundai', 'avante', 'black');
avante.color; //'black'
avante.drive();//아반떼가 운전을 시작한다.
let mini = new Car('bmw','mini','white');
mini.brand;// bmw
mini.refuel();// 미니에 연료를 공급한다.
방금 설정한 속성과 메서드를 어떻게 인스턴스에서 사용하는지 아는 것이 더 중요하다. 이 코드를 실습할 때 한 번씩 인스턴스를 console.log로 살펴보자.
용어 | 설명 |
---|---|
prototype | 모델의 청사진을 만들 때 쓰는 원형 객체(original form)이다. |
constructor | 인스턴스가 초기화될 때 실행하는 생성자 함수 |
this | 함수가 실행될 때, 해당 scope마다 생성되는 고유한 실행 context(execution context) new 키워드로 인스턴스를 생성했을 때에는, 해당 인스턴스가 바로 this의 값이 된다. |
낯선 용어가 많이 등장했다. 여기에 있는 용어는 오직 JS에서만 유ㅛ한 용어이다.
생성자 함수 정도만 객체 지향 프로그래밍에서 보편적인 개념이며, prototype이나 this는 취업을 위해서라면 언젠가는 알아야 할 것 들이지만 지금은 당장 개념에 너무 메이지 말고, 코드상에서 어떻게 쓰이는 지만 알아도 좋다.
//여기서 Car 는 class
function Car(brand, name, color){
//this 객체: 이 예제에서는 avante === this
this.brand = brand;
this.name = name;
this.color = color;
} //<-- constructor(생성자) 함수
Car.prototype.drive = function(){
//prototype 객체: 여기에 속성이나 메소드를 정의할 수 있다.
console.log(this.name + '가 운전을 시작합니다.')
}
//avante === instance
let avante = new Car('hyundai', 'avante', 'black');
avante.color; //'black'
avante.dreve(); //'avante가 운전을 시작합니다.
위에 코드를 눈에 담아두고, 외워주자. 용어를 알아두어야, 이후 설명을 들을 때에 뭐가 뭔지 헷갈리지 않게 된다. 이 코드에서는 ES5 방식을 보여주고 있지만, ES6 방식이 더욱 많이 쓰이기에, 위에 안내한 코드를 ES6로 반드시 직접 장성해 보자.
//ES6.ver
class Car{
constructor(brand, name, color){
this.brand = brand;
this.name = name;
this.coloor = color;
}
refule(){
};
drive(){
};
}
let avante = new Car('hyundai', 'avante', 'black');
avante.color; //'black'
avante.drive();//아반떼가 운전을 시작한다.
let arr = ['code','states','pre'];
arr.length; //3
arr.push('course')//새 el를 추가합니다.
클래스의 유용한 예제를 살펴보자. 사실 우린 이런 객체지향적인 방법을 이미 사용하고 있었다. 바로 우리가 그동안 만들었떤 배열은,전부 Array의 인스턴스 이다.
let avante = new Car('hyundai', 'avante', 'black');
avante.color; //'black'
avante.drive();//아반떼가 운전을 시작한다.
//우리가 배열을 정의하는 것은 Array의 인스턴스를 만들어내는 것과 동일하다.
let arr = new Array ('code','states','pre');
arr.length; //3
arr.push('course')
//mdn 메소드 설명에 prototype이라고 붙어있는 이유?
//push 구현이 원형 객체에 정의되어 있기 때문이다.
먼저 코드를 한번 살펴보자. 속성이나 메서드 사용법이 동일하다.
배열은 (대문자) Array의 인스턴스 이다.
따라서 new Array(.,~~)과 같은 방식으로도 배열을 만들 수 있다.
살짝만 더 깊게 들어가 보면, 우리가 배열 메서드(push, filter, forEach...)등을 배울 때에, mdn 문서를 살표본 사람들은, 메서드들이 대부분 Array.prototype.메서드명 과 같이 안내되어 있음을 발견했을 것이다.
이는 모든 메서드들이 클래스의 원형 객체(prototype)에 정의되어 있기 때문이다.