자바스크립트의 클래스

Sheryl Yun·2022년 9월 30일
0
post-thumbnail

예전에 자바를 잠깐 찍먹했을 때 클래스가 '객체'라는 말을 들어봤었다. 하지만 예상과 달리 자바스크립트에서의 클래스는 '객체 지향 모델'을 위한 개념이 아니었다. 그동안 클래스 코드를 볼 때마다 헷갈리고 어려웠는데 책을 통해 공부하고 나니 그리 복잡한 개념이 아니라는 걸 알게 되었다. 클래스를 생성, 상속, 수정하는 법에 대해 정리해보았다.

클래스란?

MDN에서는 자바스크립트에서의 클래스 개념을 '기존 프로토타입 기반 상속에 대한 문법적 설탕(Syntactic Sugar)'라고 말한다.

프로토타입 기반 상속

모던 자바스크립트 딥 다이브 책에서 공부했던 걸 떠올려보았다.

프로토타입 기반 상속이란, 모든 객체가 갖고 있는 상위 객체인 프로토타입에 이 객체로 만들어질 인스턴스들이 공유할 상태나 메서드를 저장해놓고 상속하는 걸 의미한다.

클래스가 이것의 '문법적 설탕'이라고 얘기한 것은 아마 프로토타입 상속의 문법이 그다지 가독성이 좋지 않아서인 것 같다.

Person.prototype.greet = function() {
	console.log("Hello, my name is " + this.name);
}

이렇게 'Person.prototype.greet' 식으로 세 단어 이상을 붙여쓴다. 또 메서드를 마치 줄글처럼 할당으로 선언해놓아 가독성이 영 떨어짐을 알 수 있다.

나중에 클래스 선언 코드를 보면 알겠지만 자바스크립트의 클래스를 사용하면 이러한 prototype 상속 문법을 훨씬 직관적이고 메서드답게 표현할 수 있다.

클래스 생성

클래스를 만드는 방법에는 클래스 선언과 클래스 표현식 두 가지가 있다. 이 중에 클래스 선언 형태를 좀 더 자주 본 것 같다. 클래스 선언과 표현식은 호이스팅이 되지 않아서 선언 전에 참조하면 에러가 발생한다.

클래스 선언

class Person {

}

클래스 표현식

함수를 변수에 담는 함수 표현식처럼 클래스를 변수에 담아 표현한다.

const person = class Person {

};

클래스 만들어보기

이제 실제로 클래스를 만들어보자. 클래스를 생성할 때 constructor는 한 번만 선언해야 한다.

class Person {
	constructor(name, age) {
    	this.name = name;
        this.age = age;
    }
    
    greet() {
    	console.log(`Hi, my name is ${this.name} and I'm ${this.age} years old`);
    }
    
    farewell() {
    	console.log('Good bye!');
    }
}

const alberto = new Person("Alberto", 26);

alberto.greet();
// Hi, my name is Alberto and I'm 26 years old
alberto.farewell();
// Good bye!

메서드를 선언하는 부분이 prototype보다 훨씬 직관적이고 간결해진 것을 볼 수 있다.

정적 메서드

static도 생각보다 간단한 개념이었다. static이 붙은 메서드는 클래스로 만들어진 인스턴스에서는 못 쓰고 오직 클래스로 직접 호출했을 때만 사용 가능하다.

class Person {
	constructor(name, age) {
    	this.name = name;
        this.age = age;
    }
    
    static info() {
    	console.log('Hi, I'm Person class');
    }
    
    const alberto = new Person("Alberto", 26);
    
    alberto.info();
    // TypeError: alberto.info is not a function
    Person.info();
    // Hi, I'm Person class
}

set과 get

클래스에서 세터와 게터 메서드를 선언할 수 있다. 세터는 클래스 내의 상태 값을 변경하고 게터는 클래스 내의 값을 단순히 가져오기만 할 때 사용한다.

class Person {
	constructor(name, surname) {
    	this.name = name;
        this.surname = surname;
        this.nickname = nickname;
    }
    
    set nicknames(value) {
    	this.nickname = value; // 클래스의 일부 상태 변경
    	console.log(`Your new nickname is ${this.nickname}`);
    }
     
    get nicknames() {
    	console.log(`Your nickname is ${this.nickname}`);
    }
 }
    
 const alberto = new Person("Alberto", "Montalesi");
 
 // 세터 호출 (새로운 값을 '할당'으로 넣음)
 alberto.nicknames = "Yena";
 
 // 게터 호출 (아무 할당 없이 그냥 메서드만 적으면 실행)
 alberto.nicknames;
 
 
 
    alberto.info();
    // TypeError: alberto.info is not a function
    Person.info();
    // Hi, I'm Person class
}

클래스 상속하기

extends 키워드를 사용하여 상위 클래스를 상속받을 수 있다.
construtor에는 super를 포함해야 하는데 이때 상속받는 상위 클래스의 상태를 super의 인자로 넣는다.
메서드는 아무 처리를 하지 않아도 자동으로 상속된다.

class Person {
	constructor(name, age) {
    	this.name = name;
        this.age = age;
    }
    
    greet() {
    	console.log(`Hi, my name is ${this.name} and I'm ${this.age} years old`);
    }
}

class Adult extends Person {
	super(name, age);
    this.work = work;
}

const alberto = new Adult("Alberto", 26, "software developer");

console.log(alberto.age); // 26
console.log(alberto.work); // software developer
alberto.greet(`Hi, my name is Alberto and I'm 26 years old`);

상속하는 하위 클래스에 super가 없으면 다음과 같은 에러가 뜬다.

ReferenceError: mush call super constructor before using |this|

하위 클래스에서 this를 쓰려면 꼭 super가 constructor에 필요하다는 내용이다.

클래스로 배열 확장하기

첫 번째 값은 교실 이름이고 나머지는 학생 이름과 학생 점수를 나타내는 다음과 같은 클래스를 만들려면 배열(Array)을 상속받아야 한다.

class Classroom extends Array {
	// rest 연산자로 교실 이름 외에 나머지 인수들을 학생들로 받는다
	constructor (classname, ...students) {
    	// 호출할 때는 spread로 풀어서 나열된 인수 형태로 super에 인자를 넣는다
    	super(...students);
        this.classname = classname;
    }
    
    // 새로운 학생을 추가하는 메서드
    add(student) {
    	this.push(student);
    }
}

// 만들려고 하는 클래스
const myClass = new Classroom('1A', 
   	{ name: "Tim", mark: 6 },
    { name: "Rosy", mark: 3 },
    { name: "Jim", mark: 8 },
    { name: "Jon", mark: 10 },
);

// 새로운 학생 추가
myClass.add({ name: "Timmy", mark: 7 });

// 인덱스 4번째에 추가된 새로운 학생을 조회(get)
myClass[4]; // { name: "Timmy", mark: 7 }

// myClass에 있는 객체들 하나씩 꺼내기
for (const student of myClass) { // 여기서 myClass는 students와 동일
	console.log(student);
}
// { name: "Tim", mark: 6 }
// { name: "Rosy", mark: 3 }
// { name: "Jim", mark: 8 }
// { name: "Jon", mark: 10 }
// { name: "Timmy", mark: 7 }
profile
데이터 분석가 준비 중입니다 (티스토리에 기록: https://cherylog.tistory.com/)

0개의 댓글