JS 공식문서 스터디 8-1. Class

CHO WanGi·2025년 9월 10일

Javascript

목록 보기
18/20

1. Class는 프로토타입과 어떤 차이를 갖나?

JS는 원래 프로토타입 기반의 객체지향 언어이다.
ES6 에 class라는 개념이 등장하긴 했지마느 Mdn 문서상 여전히 프로토타입 기반으로 작동한다

  • 기존 프로토타입을 활용하여 생성자 정의
var Person = (function () {
  // 생성자 함수
  function Person(name) {
    this.name = name;
  }

  // 프로토타입 메서드
  Person.prototype.sayHi = function () {
    console.log('Hi! My name is ' + this.name);
  };

  // 생성자 함수 반환
  return Person;
})();

클래스 문법 등장 이전에는 Person 객체를 정의하고, prototype을 활용하여 메서드를 정의했다.

  • ES6의 Class 등장
// 클래스 선언
class Person {
  // 생성자
  constructor(name) {
    this.name = name;
  }

  // 프로토타입 메서드
  sayHi() {
    console.log('Hi! My name is ' + this.name);
  }
}

// 인스턴스 생성
const me = new Person('Lee');
me.sayHi(); // Hi! My name is Lee

constructor로 생성자 함수 역할을 하고,
클래스 안에 직접 sayHi() 메서드를 정의하면 자동으로 prototype에 등록된다.

클래스 정의

class 키워드를 활용하여 정의함.

class Person {} // 선언문
const Person = class {} // 익명 클래스 표현식
const Person = class MyClass {}

클래스는 일급 객체로 다음과 같은 4가지의 특징을 갖는다.

  • 무명의 리터럴로 생성 가능

  • 변수나 자료구조에 저장 가능

  • 함수의 매개변수로 전달 가능

  • 함수의 반환값으로 사용 가능

  • class vs Prototype

클래스 호이스팅

클래스는 함수로 평가된다.
따라서 함수 선언문처럼, 클래스 역시 런타임 이전에 먼저 평가되어 함수 객체를 생성한다.
이때 클래스가 평가되어 생성된 함수 객체는 constructor

class Person {}
console.log(typeof Person) // function

단, 클래스는 클래스 정의 이전에 참조 불가능

console.log(typeof Person) // ReferencError
class Person {}

호이스팅은 발생하나, let, const 변수처럼 TDZ 로 인해 참조 불가능 한 것.

인스턴스 생성

class Person {}
// 인스턴스 설정
const me = new Person();
console.log(me) // Person{}

클래스는 인스턴스 생성이 유일한 존재 이유이니 반드시 new 연산자와 함께 호출해야함.

또한 클래스 표현식으로 정의된 경우,식별자(Person)이 아닌 기명 클래스 표현식의 이름(MyClass) 사용하면 에러가 발생

const Person = class MyClass{};
const me = new Person()

console.log(MyClass) // ReferencError
const you = new MyClass // ReferencError

메서드

클래스 몸체안에는 constructor(생성자), 프로토타입 메서드, 정적메서드 세가지 정의 가능

constructor

인스턴스 생성 및 초기화 하기 위한 특수한 메서드
생략하면, 클래스에 빈 constructor가 암묵적으로 정의 됨.

class Person {
  // 생성자
  constructor(name){
   	// 인스턴스 생성 및 초기화
    this.name = name;
  }
}

위와 같이 클래스는 평가 과정 이후 함수 객체가 된다.

  • constructor 내부 this

생성자 함수와 마찬가지로 클래스가 생성한 인스턴스를 가리킴.

  • 인스턴스 생성시 외부에서 프로퍼티의 초기값을 전달
class Person{
  constructor(para_name, para_address){
    this.name = para_name;
    this.address = para_address
  }
}

const me = new Person('Lee', 'Seoul')

constructor는 암묵적으로 this, 즉 인스턴스를 반환한다.
만약 이를 무시하고 특정값을 반환하고자 하면 return 키워드를 사용.

프로토타입 메서드

위에서 봤던 sayHi() 메서드를 말한다.
클래스가 생성한 인스턴스 역시 프로토 타입 체인의 일원이 된다.

정적(static)메서드

인스턴스를 생성하지 않아도 호출 가능한 메서드

class Person{
 constructor(name){
  this.name = name; 
 }
 static sayHi(){
  console.log('Hi'); 
 }
}

class me = new Person('Lee')
Person.sayHi() // Hi!
me.sayHi() // TypeError

정적 메서드는 클래스에 바인딩 된 메서드가 되어, 인스턴스로 호출 불가.

정적메서드 vs 프로토타입 메서드

-> 3가지 차이가 있다.
1. 속해있는 프로토 타입 체인이 다르다
2. 정적 메서드는 클래스로 호출하고, 프로토타입 메서드는 인스턴스로 호출한다.
3. 정적 메서드는 인스턴스의 프로퍼티 참조가 불가능하지만, 프로토타입 메서드는 가능하다

  • 프로토타입
class Square {
  constructor(width, height) {
    this.width = width;
    this.height = height;
  }

  // 프로토타입 메서드
  area() {
    return this.width * this.height;
  }
}

const s = new Square(10, 10);
console.log(s.area()); // 100

인스턴스인 s가 사용 가능하다.
this는 인스턴스 자신(s)이어서 this.width, this.height 접근 가능

  • 정적 메서드
class Square {
  constructor(width, height) {
    this.width = width;
    this.height = height;
  }

  // 정적 메서드
  static description() {
    return '이 클래스는 사각형을 표현합니다.';
  }
}

console.log(Square.description()); // 클래스 이름으로 직접 호출
const s = new Square(10, 10);
console.log(s.description()); // 불가

클래스에 직접 바인딩되기에 클래스 이름으로 직접 호출해야한다.
인스턴스로는 호출이 불가능.

프로퍼티

접근자 프로퍼티

값을 읽거나 저장할때 사용하는 프로퍼티
쉽게 우리가 자주 쓰는 getter, setter 를 말한다

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  // getter: fullName을 '읽을 때' 실행됨
  get getFullName() {
    return `${this.firstName} ${this.lastName}`;
  }

  // setter: fullName에 '값을 할당할 때' 실행됨
  set setFullName(name) {
    [this.firstName, this.lastName] = name.split(' ');
  }
}

const me = new Person("Lee", "Wangi");

// getter 사용
console.log(me.getFullName);  
// 'Lee Wangi' (함수처럼 호출 X → 프로퍼티처럼 접근)

// setter 사용
me.setFullName = "Kim Jisoo";
console.log(me.firstName); // 'Kim'
console.log(me.lastName);  // 'Jisoo'

만약 get/set 설정이 없다면 프로퍼티처럼 못쓰고 ()를 붙여서 명시적으로 함수 호출이 필요하다.

  // 일반 메서드
  fullName() {
    return `${this.firstName} ${this.lastName}`;
  }

  setFullName(name) {
    [this.firstName, this.lastName] = name.split(' ');
  }
}

const me = new Person("Lee", "Wangi");
console.log(me.fullName());    // 메서드라서 () 꼭 필요
me.setFullName("Kim Jisoo");   // 메서드 호출로만 값 변경 가능
console.log(me.firstName);     // 'Kim'
console.log(me.lastName);      // 'Jisoo'

Private(#)

JS는 다른 언어들에서 지원하는 private, public, protected 같은 키워드를 지원 X
인스턴스 프로퍼티는 인스턴스를 통해 클래스 외부에서 언제나 참조 가능하기에 Public

ES2022 에서 도입된 Private 필드 문법 # 이 도입

class Person {
  // private 필드 정의 (#)
  #name = '';

  constructor(name) {
    // private 필드 참조 (클래스 내부)
    this.#name = name;
  }
}

const me = new Person('Lee');

// private 필드는 클래스 외부에서 접근 불가
console.log(me.#name); 
// ❌ SyntaxError: Private field '#name' must be declared in an enclosing class

이것만 쓰면 이게 왜 필요한지 모른다.
getter, setter를 함께 사용하면 그진가가 나온다

class Person {
  #name;

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

  get name() {
    return this.#name;
  }

  set name(newName) {
    if (newName.length > 0) {
      this.#name = newName;
    }
  }
}

const me = new Person("Lee");
console.log(me.name); // 'Lee'

me.name = "Kim";      
console.log(me.name); // 'Kim'

console.log(me.#name); // ❌ 에러

getter/setter로만 접근가능하도록 하여, name이란 데이터가 외부로부터 오염되지 않도록
안전하게 활용 가능.

상속과 확장 : extends

class Animal {
  constructor(age, weight) {
    this.age = age;
    this.weight = weight;
  }

  eat() { return 'eat'; }
  move() { return 'move'; }
}

// Animal 클래스를 확장한 Bird 클래스
class Bird extends Animal {
  fly() { return 'fly'; }
}

상속 키워드인 extends 활용을 통해 부모 클래스의 속성과 메서드 재사용 가능

profile
제 Velog에 오신 모든 분들이 작더라도 인사이트를 얻어가셨으면 좋겠습니다 :)

0개의 댓글