extends
getter
setter
를 배우기 전에
class
prototype
constructor
가 무엇인가에 대한 개념부터 확실히 짚고 넘어가야 한다.
Class
객체를 생성하기 위한 일종의 템플릿 또는 기계. Class는 속성과 메서드를 정의하여 객체의 동작을 결정하고 객체를 생성하기 위한 청사진(blueprint) 역할을 한다고 볼 수있다.
Prototype
객체 간의 상속과 생성자 함수에 정의한 모든 객체가 공유할 원형이다. 자바스크립트는 프로토타입 기반 언어로, 객체는 프로토타입을 가지며, 해당 프로토타입의 속성과 메서드를 공유한다. 객체의 프로토타입은 해당 객체를 생성한 클래스(또는 생성자 함수)의 prototype 속성에 저장된다. 객체가 어떤 속성이나 메서드를 찾을 때, 상위 프로토타입으로 올라가며 탐색하는 방식.
Constructor
클래스로부터 객체를 생성할 때 호출되는 메서드이다. 클래스 내부에 정의되며, 객체의 초기 상태를 설정한다. 또한, 클래스 내부에서 "constructor"라는 이름의 메서드로 정의된다. 객체가 생성될 때 Constructor는 자동으로 호출되며, 객체의 속성을 초기화하거나 다른 초기화 작업을 수행할 수 있다.
클래스를 상속하는 클래스를 만들고 싶을 때 사용하며, 흔히들 조상, 부모 클래스로 코드를 작성하는 예시를 사용한다. 하지만 자바 강의를 들을 때 너무나 많이 사용하여 좀 질리기도 하고, 동물을 주제로 살펴보기로 한다.
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name}가 소리를 냅니다.`);
}
}
class Dog extends Animal {
constructor(name) {
super(name); // 부모 클래스의 생성자 호출
}
speak() {
console.log(`${this.name}가 멍멍하며 짖습니다.`); // 자식 클래스의 메서드 오버라이딩
}
}
const dog = new Dog("멍멍이");
dog.speak(); // 출력: 멍멍이가 멍멍하며 짖습니다.
Animal
클래스는 동물을 나타내는 기본 클래스다. Dog
클래스는 Animal
클래스를 상속받아 확장한 자식 클래스다. 상속을 통해 Dog
클래스는 Animal
클래스의 속성과 메서드를 모두 사용할 수 있다.
extends 키워드를 사용하여 클래스의 상속 관계를 정의한다. Dog extends Animal
은 Dog 클래스가 Animal 클래스를 상속받는다는 뜻.
constructor 내부에서 super(name)
을 호출하여 부모 클래스의 생성자를 호출한다. 이렇게 하면 부모 클래스의 속성을 초기화할 수 있다.
자식 클래스인 Dog에서 speak()
메서드를 다시 정의하여 메서드 오버라이딩한다. 즉, 부모 클래스의 동일한 이름의 메서드를 자식 클래스에서 재정의한다.
결국 동물 클래스를 예시로 들어도, 부모-자식 키워드를 대신 하기엔 아직 힘든 것 같다.
Q. 자식 클래스에서 super()를 생략하면 어떻게 될까?
A. TypeError: Cannot read property 'name' of undefined
- 객체에서 새로 만들어진 dog 인스턴스는
name
속성이 정의되지 않았다.super()
를 통해 부모 클래스의 생성자를 호출하여 초기화 작업을 수행하지 않았기 때문.- 즉,
super()
는 부모 클래스의 생성자를 호출하는 역할을 한다.- 더 쉽게 말해서, extends로 상속중인 부모 class의 constructor() == Animal 클래스의 constructor()
상속을 통해 자식 클래스는 부모 클래스의 속성과 메서드를 상속받을 뿐만 아니라, 필요에 따라 재정의 할 수 있다.
var 사람 = {
name : 'Kim',
age : 30,
get nextAge(){
return this.age + 1
}
}
console.log( 사람.nextAge ) //get 키워드를 추가하면 이렇게 함수를 사용가능
값을 받아올 때 사용하며 getter는 호출하는 것이 아니라 프로퍼티처럼 참조하는 형식으로 사용하며 참조 시에 메소드가 호출된다. getter는 이름 그대로 무언가를 취득할 때 사용하므로 return
이 붙는다.
class 사람 {
constructor(){
this.name = 'Park';
this.age = 20;
}
get nextAge(){
return this.age + 1
}
set setAge(나이){
this.age = 나이;
}
}
var 사람1 = new 사람();
데이터를 할당할 때 사용하며 메소드 이름 앞에 set 키워드를 사용해 정의한다. 이때 메소드 이름은 클래스 필드 이름처럼 사용된다. setter는 호출하는 것이 아니라 프로퍼티처럼 값을 할당하는 형식으로 사용하며 할당 시에 메소드가 호출된다.
데이터 접근의 편의성
객체 안에 있는 데이터가 복잡할수록, getter 메서드를 사용하여 데이터를 추출하는 것이 편하다. getter 메서드를 통해 필요한 데이터를 간편하게 가져올 수 있으며, 복잡한 로직을 내장하여 필요한 가공 작업을 수행할 수도 있다.
안전성과 실수 방지
getter와 setter를 사용하면 외부에서 직접적으로 객체의 내부 변수에 접근하지 않고 메서드를 통해 값을 가져오거나 설정할 수 있다. 그러므로 내부 변수에 실수로 잘못된 값을 대입하는 것을 방지할 수 있다. getter와 setter를 통해 데이터에 접근하므로, 객체의 내부 구조를 더 안전하게 유지할 수 있다.
코드 재사용과 유지보수의 용이성
긴 객체에 대해서 원하는 데이터만 추출하고자 할 때, getter 메서드를 미리 만들어놓으면 반복적인 기능 개발을 피할 수 있다. getter 메서드를 활용하여 원하는 데이터만 쉽게 추출하고 가공할 수 있으며, 이는 코드의 재사용성과 유지보수성을 좋게 한다.
class Product {
constructor(name, price, quantity) {
this.name = name;
this.price = price;
this.quantity = quantity;
}
calculateTotalPrice() {
return this.price * this.quantity;
}
}
const product = new Product("Apple", 2.5, 10);
console.log(product.name); // 출력: Apple
console.log(product.price); // 출력: 2.5
console.log(product.quantity); // 출력: 10
product.quantity = 5;
console.log(product.quantity); // 출력: 5
product.quantity = -3; // 문제 발생!
console.log(product.quantity); // 출력: -3
console.log(product.calculateTotalPrice()); // 출력: -7.5 (오류 발생)
quantity
속성에 접근할 때 setter를 사용하지 않고 직접 값을 변경할 수 있으므로, 음수 값을 할당할 수 있다. 실제로는 허용되지 않는 수량이지만, 코드에서는 문제가 발생하지 않고 계산이 이루어진다. 따라서 product.quantity = -3
과 같이 음수 값을 할당하는 실수로 인해 잘못된 데이터가 저장되고, 계산 결과도 오류가 발생하게된다. 이런 상황에서는 getter와 setter를 사용하여 유효성 검사나 추가 로직을 수행하고, 실수로 잘못된 값을 할당하는 것을 방지할 수 있다.