[TIL / DAY 14] 클래스

miseullang·2024년 10월 31일
post-thumbnail

✅ 강의 내용 정리

📍 클래스


생성자 함수 방식과 클래스로 인스턴스 생성

아래는 같은 내용을 생성자 함수와 클래스로 각각 정의한 예제이다.
자바스크립트의 클래스는 프로그래머의 편의를 위한 문법적 설탕 개념으로, 동작 자체를 분석해보면 결국 생성자 함수 방식으로 동작한다.

** 문법적 설탕

이라는 표현은 자바스크립트 클래스가 내부적으로 프로토타입 기반의 동작을 감싸서 좀 더 직관적인 문법을 제공한다는 것을 의미한다. 즉, 클래스는 함수를 문법적 설탕으로 포장해서 편리한 문법을 제공하는 것이므로, 생성자 함수와 프로토타입을 통해 인스턴스를 생성하는 것과 동일한 방식으로 작동하는 것이다.

// 생성자 함수 (ES5 버전에서 사용하던 방식)
function PersonF(name, age) {
  this.name = name;
  this.age = age;
}

// 생성자 함수로 인스턴스 생성
const personF = new PersonF("토토로", 140); // PersonF { name: '토토로', age: 140 }
console.dir(personF);

// --------------(위 아래는 같다)----------------

// 클래스 (ES6 버전 이후 사용)
class Person {
  constructor(name, age) { // 초기 멤버 속성 정의
    this.name = name;
    this.age = age;
    console.log(name, age);
  }
}

// new 키워드를 이용해서 클래스 인스턴스 생성
const personC = new Person("영희", 20);
console.dir(personC); // Person { name: '영희', age: 20 }

클래스의 정의와 상속

cunstructor

클래스를 정의할 때 초기 멤버가 필요하다면 constructor를 사용해야 한다. 강제되는 내용이 아니므로, 멤버 속성이 필요하지 않다면 사용하지 않아도 된다.

extends

상속을 정의하는 키워드이다.
class 상속받는 클래스 extends 상속하는 클래스 형태로 작성하면 된다.

생성자 함수 방식클래스
초기 구문이 비교적 복잡함코드의 간결성
명시적으로 프로토타입을 설정해야 함상속이 더 쉬움

extends를 사용할 때, constructor를 사용한다면 내부에 super()를 반드시 적어줘야 한다. constructor를 정의할 때는 super()를 호출해야만 부모 클래스의 속성을 사용할 수 있기 때문이다.

⇒ 인스턴스에 부모의 인스턴스를 포함시키는 것. 내부적으로 프로토타입 체이닝이 발생하는 것이다.

🤖 super()의 역할에 대해 추가로 설명해줘

super()부모 클래스의 생성자를 호출하는 역할을 합니다.

자식 클래스에서 부모 클래스의 기능을 상속받고, 그 위에 추가적인 속성이나 메서드를 정의할 때 super()를 호출하여 부모 클래스의 초기화 작업을 수행해야 합니다. 만약 super()를 호출하지 않으면 자식 클래스의 this에 접근할 수 없으므로, 자식 클래스의 생성자 내에서 this를 사용하는 경우 반드시 super()를 먼저 호출해야 합니다.


static

정적 메서드란, 인스턴스를 사용하지 않고 사용할 수 있는 메서드를 말하며, static을 사용해서 정적 메서드를 만들 수 있다.

static 키워드를 사용하여 정의된 메서드는 클래스 인스턴스를 생성하지 않고도 호출할 수 있다. 정적 메서드란, 인스턴스를 사용하지 않고 클래스 이름을 통해 직접 호출할 수 있는 메서드를 의미한다.

** 정적 메서드의 장점
불필요하게 인스턴스를 사용하지 않고 바로 메서드로 접근하게 해줌으로써, 메모리 효율을 높일 수 있다.

class MathUtils {
  // 정적 메서드: 클래스 인스턴스를 생성하지 않고도 호출 가능
  static add(n1, n2) {
    return n1 + n2;
  }
}

// const math = new MathUtils(); // 정적 메서드로 정의된 클래스의 프로퍼티는 이렇게 사용할 수 없다
const sum = MathUtils.add(10, 20); // 정적 메서드는 클래스 이름을 통해 호출해야 한다
console.log(sum); // 30

// ============================

// 생성자 함수로 정적 메서드 생성하기
function Maths() {}
Maths.add = function (a, b) {
  return a + b;
};
console.log(Maths.add(10, 20)); // 30

getter / setter

getter / setter는 접근자 혹은 접근자 메서드라고 부른다.
이것은 인스턴스 객체의 속성 값을 바꿀 때, 바꾸는 것을 제어하고 싶은 경우에 사용한다.

set이 사용될 때 반드시 get의 사용이 강제되지는 않는다.

  • getter : 속성 값을 조회할 때 호출되며, 추가 로직을 통해 속성 값을 가공하거나 제한할 수 있다.
  • setter : 속성 값을 설정할 때 호출되며, 값의 유효성을 검사하거나 특정 조건에 따라 값을 변경할 수 있다.
    • set 키워드를 사용했을 때는 반드시 매개변수를 하나만 등록해야 한다. (두 개 이상은 구문 오류)
class Car {
  constructor(speed) {
    this.speed = speed; // 초기 속도 설정 (setter가 호출됨)
  }

  // setter: speed 속성에 값을 할당할 때 호출됨
  set speed(value) {
    // 내부에서 다시 speed를 호출하므로 재귀 호출 문제가 발생
    this.speed = value < 0 ? 0 : value;
  }
}

const car = new Car(200);
car.speed = -100;

console.log(car.speed); // 재귀 호출로 인해 에러 발생

// =========== 해결 방법 ==================

class Car {
  constructor(speed) {
    this._speed = speed; // 초기 속도 설정
    this._color = color;
  }

  // setter: speed 속성에 값을 할당할 때 호출됨
  set speed(value) {
    // 내부에서 _speed를 사용하여 재귀 호출을 피함
    this._speed = value < 0 ? 0 : value; // speed 값이 음수가 되지 않도록 함
  }

  // getter: speed 속성을 조회할 때 호출됨
  get speed() {
    return this._speed;
  }

  set color(value) {
    this._color = value === "white" ? "black" : value;
  }

  // getter: speed 속성을 조회할 때 호출됨
  get color() {
    return this._color;
  }
}

const car = new Car(200);
car.speed = -100; // => set speed()가 호출되어 _speed 값이 설정됨

console.log(car.speed); // => get speed()가 호출되어 _speed 값을 반환함

프라이빗 필드

클래스 내부에서만 접근할 수 있는 속성으로, # 기호를 붙여서 선언한다.

외부에서는 직접 접근이 불가능하고, 상속된 클래스에서도 마찬가지로 접근할 수 없다.
⇒ 따라서 클래스 내부의 데이터를 안전하게 보호하고, 외부에서 직접 변경하지 못하도록 할 때 사용한다.

class Car {
  #speed; // 프라이빗 필드

  constructor(speed) {
    this.#speed = speed;
  }

  getSpeed() {
    return this.#speed; // 클래스 내부에서만 접근 가능
  }
}

const car = new Car(100);
console.log(car.getSpeed()); // 100
console.log(car.#speed); // 오류 발생: 접근 불가

다른 클래스에 상속되어도 프라이빗 필드는 똑같이 유지된다

오버라이딩

메서드를 재정의하는 현상(= 덮어쓰기)이다.


class Animal {
  sound() {
    console.log("sound!");
  }
}

class Dog extends Animal {
  run() {
    console.log("run");
  }

  sound() {
    console.log("dog sound");
  }
}

const dog = new Dog();
dog.run();
dog.sound();

// 실행 결과

// run
// dog sound






➕ 추가 개념

📍 일급 객체


정의

객체는 아니지만 객체로 취급이 되는 것

일급 객체가 되기 위한 조건

일급 객체란, 변수에 할당하거나 함수의 인자로 전달할 수 있는 특성을 가진 객체

  1. 변수에 할당이 가능해야 한다
// 함수 표현식
const sum = function (num1, num2) {
	return num1 + num2;
}
  1. 함수의 인자로 전달할 수 있어야 한다
function greet(callback) {
	callback();
}

greet(() => {console.log("hello"});
  1. 함수의 반환 값으로 사용할 수 있어야 한다
function outer() {
	return function(){
		console.log("inner");
	}
};

const a = outer();
a();
  1. 동적으로 생성이 가능해야 한다
const Func = new Function("name", "return '안녕하세요, ' + name");
console.log(Func("기수"));




📍 메서드 체이닝


메서드 체이닝은 메서드 호출이 끝난 후 객체 자신을 반환하여 다음 메서드를 연속해서 호출할 수 있게 하는 패턴이다. 따라서, 메서드 체이닝을 사용하려면 this를 사용하는 것이 기본이다.

class Builder {
  constructor() {
    this.value = "";
  }

  append(str) {
    this.value += str;
    return this; // 체이닝을 위해 this 반환
  }

  getValue() {
    return this.value; // 최종 값을 반환하고 체이닝 종료
  }
}

// 테스트 케이스
const builder = new Builder();
const result = builder.append("Hello, ").append("World!").getValue();
console.log(result); // Hello, World!
profile
괴발개발 💻

0개의 댓글