자바스크립트 클래스랑 친해지기가 참 어렵다.. 봐도봐도 낯설다.
함수에서 prototype으로 연결하고 싶은 마음이 너무나 굴뚝같지만 이번 기회에 클래스와 친해져보기로 했다.
사실 자바스크립트 function으로 다 해결할 수 있지 않나?
라고 생각해왔다.
function은 new 키워드를 사용하면 생성자 함수로도 쓸 수 있고, 일반 함수로도 쓸 수 있고, 객체 내부 메서드로도 사용될 수 있다는 점에서 무의식적으로 function
만 찾게 되었던 것 같다.
일단 클래스는 뭔가 모를 거부감이 드는 것도 있었는데.. 아마 자주 사용해본 적이 없으니 당연히 쓰던 것만 쓰게 된 게 아닐까? function 생성자 함수 대신 Class를 사용하는 방법을 공부해보고자 한다.
/* Class로 정의한 Person */
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
Person
클래스를 정의하였다.
그렇다면 같은 것을 function으로도 만들어보자.
/* function으로 정의한 Person */
function Person(name, age){
this.name = name;
this.age = age;
}
function
은 호이스트되기 때문에 다음과 같은 코드로 작성도 된다.
const person = new Person('아이유', 30);
function Person(name, age){
this.name = name;
this.age = age;
}
console.log(person)
얼핏 보면 음 ? 뭐가 문제가 될까?
싶은 코드여서 혼자 고민을 해보았다.
예전에 봤던 책에서 클래스를 붕어빵 틀로 비유했던 예시가 떠올라서 나름대로 해당 비유에 이 코드를 적용시켜보았다.
Class와 비교해보자면 여기서 생성자 함수를 붕어빵 기계라고 빗댔을 때 보통은 붕어빵을 만들 기계가 있어야 붕어빵을 만들 수 있다. 그런데 저 코드는 붕어빵부터 만든거다.
붕어빵 기계에 반죽을 넣어서 붕어빵을 만들기도 전에 붕어빵부터 만들고 기계를 만들었다
고 생각하면 뭔가 이상하지 않은가? 본인은 그렇게 생각해서 Class를 이해했다. ㅋㅋㅋㅋ
본인은 function이 가지는 호이스팅을 장점이라 생각했다.
무엇보다 이 호이스팅 덕분에 하단에 함수를 미리 정의 해놓고 상단에서 함수를 호출하는 방식을 선호했었다.
하지만, 얼마 전 에어비앤비 코드 컨벤션 보면서 생각이 바뀌게 된 계기가 있었는데 지금 생성자 함수로 사용시 function을 보니 조금 느낌이 다른 것 같아서 왜 Class가 등장했는지 알 것 같기도 하다.
Class
는 정의하기 전에 선언하여 사용할 수 없다.
const person = new Person('아이유', 30);
/* Uncaught ReferenceError: Person is not defined */
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
Class로 만든 Person으로 인스턴스를 만드려고 하자
Person이 defined 되지 않았다고 바로 에러를 뱉는다.
앞선 생성자 함수는 붕어빵 기계가 있기도 전에 붕어빵을 만들었다면
Class는 붕어빵 기계가 없기 때문에 붕어빵을 만들 수 없어!
라고 화부터 낸다.
MDN 문서로 공부하던 중 Class 표현식이라는 게 있어..? 이거 가능..?
정말 놀랍게도 Class 표현식이 가능하다는 것이다.
생각해보니 Class도 내부적으로는 function prototype으로 동작하는 것으로 알고 있어서 가능한 것 같다.
그런데 여기서 궁금한 부분이 생겼다.
class 표현식 역시 호이스팅 제한이 적용된다고?
그러면 var
로 만든 Class는 어떻게 동작할까?
라는 궁금증이다.
이건 안 되나보다.
대신 var로 class를 선언하게 되면 전역 class가 되어버린다.
var Person = class {
constructor(name, age){
this.name = name;
this.age = age;
}
}
var
를 쓸 일은 없지만 궁금한건 못 참지 ㅎ
아무튼 다시 본론으로..
표현식 Class도 익명 또는 기명으로 만들 수 있다.
이름을 가지고 있는 Class 표현식은 클래스 body의 로컬 스코프에 한해 유효한다고 적혀있었다.
아니 이게 무슨 말이지?
Test 바깥에서 Person 클래스에 접근할 수 없다는 의미인가..보다.
클래스 내부에서만 접근 가능하다고 되어 있어서 내부에서 Person2 를 찍도록 해봤다.
이 부분은 이해하는데 조금 애매한 것 같아서 일단 아 그렇구나. 하고 필요할 때 다시 꺼내봐야겠다.
Test.name으로 Person에 접근할 수는 있지만 바깥에서 Person을 찾을 수는 없는듯하다.
로컬 스코프라서 그런거겠지만.
무명 클래스인 경우 .name
했을 때 변수 이름이 출력되고 기명 클래스는 클래스 이름이 출력된다.
사실 클래스를 처음 봤을 때 이 알수없는 constuctor
는 도대체 뭘까..? 라고 생각했었다.
그냥 쓰니까 나도 써야되나보다 했었는데 언젠가부터 컨스트럭터에 대해 자연스럽게 이해가 된 것 같다.
constructor는 class로 만든 인스턴스를 생성할 때 초기화 해주는 친구이다. 그렇기에 클래스 하나당 딱 하나만 존재할 수 있다. (여러개 있으면 에러난다.)
엄마와 아들 클래스를 만들었다.
엄마 클래스를 상속받게 할 것이기 때문에 컨스트럭터는 엄마 클래스에서 만들어보았다.
아들 클래스에서도 컨스트럭터를 만들고 super 키워드를 사용해서 아들 클래스에서 추가로 필요한 컨스트럭터를 넣을 수도 있겠지만, 메서드에서 super를 사용해보기로 했다.
class Mother {
constructor(name, sentence){
this.name = name;
this.sentence = sentence;
}
say(){
console.log(`${this.name} : ${this.sentence}`);
}
}
class Son extends Mother {
say(){
super.say();
console.log(`${this.name} : 감잔가?`)
}
}
const son = new Son('정준하', '엄마 이거 뭐에요?');
son.say();
const mother = new Mother('나문희', '고구마호박');
mother.say();
여기서 Son 클래스에서 super
를 호출했다.
say 메서드를 상속받아 사용함과 동시에 추가적으로 Son 클래스에서만 사용할 수 있는 말을 덧붙여보도록 했다.
super
는 부모 클래스의 것을 사용한다는 의미로 쓰인다.
예를 들어 위 코드에서는 Mother 클래스의 say 메서드를 호출하면서 감잔가?
라는 말을 추가로 할 수 있도록 작성했다.
만약 Son클래스에서 super.say()
를 생략했다면 정준하 : 감잔가?
만 출력될 것이다.
즉, 부모의 메서드를 사용하면서 내것(자식) 메서드에 부가적인 기능을 추가할 때 super
를 사용해 볼 수 있을 것이다.
인스턴스를 만들지 않고도 사용하고 싶다면 static
솔직히 클래스에 대해 무지할 때 이게 뭔소리지.. 인스턴스는 뭐꼬 스태틱은 뭔데..ㅎ 했던 적이 있었다.
인스턴스는 new 키워드를 통해 만들어진 것을 의미한다.
static으로 선언하면 new 어쩌구
와 같이 new 키워드 없이도 사용할 수 있다.
class Animal{
static run(){
console.log('뛰는중...');
}
}
Animal.run();
class Animal2{
run(){
console.log('뛰는중...');
}
}
Animal2.run(); // 에러 발생
Animal과 Animal2 클래스로 비교해보자.
Animal 클래스의 run은 static
으로 선언된 메서드이다.
즉, 인스턴스(new Animal)를 만들지 않아도 사용할 수 있기 때문에 Animal.run()으로 호출할 수 있게 된다.
그렇다면 이번엔 Animal2 클래스를 보자.
static으로 선언되지 않은 메서드이므로 인스턴스를 만들어 주어야 한다.
하지만 인스턴스를 만들지 않고 run을 호출하고 있다.
에러가 발생한다.
class Animal{
static run(){
console.log('뛰는중...');
}
}
Animal.run();
const animal = new Animal();
animal.run(); // 에러
보통 static으로 만든 메서드가 아니였다면 animal.run()과 같이 run 메서드를 호출했을 것이다.
하지만 static으로 선언된 메서드는 인스턴스로 호출할 수 없기 때문에 에러
가 발생한다.
static은 인스턴스를 만들지 않고 사용할 수 있기 때문에 유틸리티 함수, 정적 속성인 경우 인스턴스 간에 복제할 필요가 없는 데이터(똑같은 것을 공유해서 쓸 때)를 만들 때 사용된다.
오늘은 자바스크립트 Class와 친해지기 위해 가볍게 Class 기본적인 사용 방법에 대해 공부해보았다.
class를 사용하는 언어에 능숙하다면 자바스크립트 class도 어렵지 않게 이해할 수 있겠지만... 나는 그렇지 않으므로ㅠㅠㅠ
문서를 읽어보니 생각보다 어려운 내용이 많아서 한번에 다 이해하려고 하기 보다는 천천히 읽어보면서 이해해봐야겠다.
특히 public이나 private.. 캡슐화라던가..
입문자에게는 다소 어렵게 느껴지는 OOP개념에 대해서 익숙해지려면 시간이 필요할듯하다. ㅎㅎ
오늘은 Class를 공부하셨군요. 중간에 예제 부분에서 웃겨서 수명이 5분정도 늘어난 것 같습니다.^^
오늘도 좋은 글 잘 읽고 갑니다. 제 수명까지 늘려주시고 감사합니다