반응형
Jake Seo
제이크서 위키 블로그
Jake Seo
전체 방문자
오늘
어제
  • 분류 전체보기 (715)
    • 일상, 일기 (0)
    • 백준 문제풀이 (1)
    • 릿코드 문제풀이 (2)
    • 알고리즘 이론 (10)
      • 기본 이론 (2)
      • 배열과 문자열 (8)
    • 데이터베이스 (15)
      • Planet Scale (1)
      • MSSQL (9)
      • 디비 기본 개념 (1)
      • SQLite 직접 만들어보기 (4)
    • 보안 (7)
    • 설계 (1)
    • 네트워크 (17)
      • HTTP (9)
      • OSI Layers (5)
    • 회고 (31)
      • 연간 회고 (2)
      • 주간 회고 (29)
    • 인프라 (52)
      • 도커 (12)
      • AWS (9)
      • 용어 (21)
      • 웹 성능 (1)
      • 대규모 서비스를 지탱하는 기술 (9)
    • 깃 (7)
    • 빌드 도구 (7)
      • 메이븐 (6)
      • 그레이들 (0)
    • Java (135)
      • 이펙티브 자바 (73)
      • 자바 API (4)
      • 자바 잡지식 (30)
      • 자바 디자인 패턴 (21)
      • 톰캣 (Tomcat) (7)
    • 프레임워크 (64)
      • next.js (14)
      • 스프링 프레임워크 (28)
      • 토비의 스프링 (6)
      • 스프링 부트 (3)
      • JPA (Java Persistence API) (5)
      • Nest.js (8)
    • 프론트엔드 (48)
      • 다크모드 (1)
      • 노드 패키지 관리 매니저 (3)
      • CSS (19)
      • Web API (11)
      • tailwind-css (1)
      • React (5)
      • React 새 공식문서 요약 (1)
      • HTML (Markup Language) (5)
    • 자바스크립트 (108)
      • 모던 자바스크립트 (31)
      • 개념 (31)
      • 정규표현식 (5)
      • 코드 스니펫 (1)
      • 라이브러리 (6)
      • 인터뷰 (24)
      • 웹개발자를 위한 자바스크립트의 모든 것 (6)
      • 팁 (2)
    • Typescript (49)
    • 리눅스와 유닉스 (10)
    • Computer Science (1)
      • Compiler (1)
    • IDE (3)
      • VSCODE (1)
      • IntelliJ (2)
    • 세미나 & 컨퍼런스 (1)
    • 용어 (개발용어) (16)
      • 함수형 프로그래밍 용어들 (1)
    • ORM (2)
      • Prisma (2)
    • NODEJS (2)
    • cypress (1)
    • 리액트 네이티브 (React Native) (31)
    • 러스트 (Rust) (15)
    • 코틀린 (Kotlin) (4)
      • 자바에서 코틀린으로 (4)
    • 정규표현식 (3)
    • 구글 애널리틱스 (GA) (1)
    • SEO (2)
    • UML (2)
    • 맛탐험 (2)
    • 리팩토링 (1)
    • 서평 (2)
    • 소프트웨어 공학 (18)
      • 테스팅 (16)
      • 개발 프로세스 (1)
    • 교육학 (1)
    • 삶의 지혜, 통찰 (1)
    • Chat GPT (2)
    • 쉘스크립트 (1)
    • 컴파일 (2)
    • Dart (12)
    • 코드팩토리의 플러터 프로그래밍 (4)
    • 플러터 (17)
    • 안드로이드 스튜디오 (1)
    • 윈도우즈 (1)
    • 잡다한 백엔드 지식 (1)
    • 디자인 패턴 (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • NEXT JS
  • 팩터리 메서드 패턴
  • pnpm
  • 빈 검증
  • 느린 쿼리
  • Pre-rendering
  • 이펙티브 자바 item9
  • MSSQL
  • serverless computing
  • 도커공식문서
  • 추상 팩터리 패턴
  • Java
  • 자바 디자인패턴
  • 작업기억공간
  • Next.js
  • Javadoc 자바독 자바주석 주석 Comment
  • item7
  • 자바스크립트 인터뷰
  • 프로그래머의 뇌
  • try-with-resources
  • 참조 해제
  • rust
  • 스프링 검증
  • 알고리즘
  • 러스트
  • 자바
  • 서버리스 컴퓨팅
  • 싱글톤 패턴
  • item9
  • 싱글톤
  • prerendering
  • bean Validation
  • 메이븐 골
  • 디자인패턴
  • 이펙티브 자바
  • 메이븐 라이프사이클
  • 싱글턴
  • 슬로우 쿼리
  • 메이븐 페이즈
  • 토비의 스프링
  • 자바 검증
  • next js app
  • 자바스크립트
  • 외래키 제약조건
  • item8
  • 자료구조
  • 자바스크립트 면접
  • 객체복사
  • 이펙티브자바
  • 플라이웨이트패턴

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
Jake Seo

제이크서 위키 블로그

자바스크립트/모던 자바스크립트

자바스크립트 클래스 2 - 클래스 바디

2023. 2. 1. 23:38

클래스 바디의 필드와 메서드

  • 클래스 바디란 {} 내부의 요소들을 말한다.
  • 클래스 바디엔 대표적으로 상태를 저장하는 필드와 상태를 이용해 동작을 수행하는 메서드가 있다.

클래스의 바디에 올 수 있는 요소의 경우의 수

  • 종류: Getter, Setter, Method, Field
  • 위치: Static, Instance
  • 접근: Public, Private

클래스의 바디에 오는 요소는 위 3가지를 모두 가져야 한다. 총 16가지 조합이 가능하다.

클래스에만 있는 특별한 것들

  • 생성자
  • 정적 초기화 블록

클래스 필드 추가하기

class ExampleClass {
  constructor() {
    this.field = 1;
  }
}

const c = new ExampleClass();
console.log(c.field); // 1
  • 예전엔 생성자에서 this.fieldName = fieldValue 와 같은 문법으로 필드를 추가해야 했다.
class ExampleClass {
  field = 1;
}

const c = new ExampleClass();
console.log(c.field); // 1
  • 이제는 위와 같은 문법으로도 추가 가능하다.
  • 기본 할당은 자연적으로 구성, 쓰기, 열거를 지원한다.

클래스 private 필드

class ClassWithPrivateField {
  #privateField;

  constructor() {
    this.#privateField = 42;
  }

  getPrivateField() {
    return this.#privateField;
  }
}

const c = new ClassWithPrivateField();
console.log(c.getPrivateField()); // 42
console.log(c.#privateField); // Uncaught SyntaxError: Private field '#privateField' must be declared in an enclosing class
  • private field 는 클래스 외부에서 접근이 불가능한 필드이다.
  • 클래스 내부에서만 접근이 가능하다.
class ClassWithPrivateField {
  #privateField;

  constructor() {
    this.#privateField = 42;
    this.#randomField = 666; // Uncaught SyntaxError: Private field '#randomField' must be declared in an enclosing class
  }
}
  • private field 는 일반 필드와 다르게 반드시 위에서 먼저 선언 후에 사용해야 한다.

클래스 private 메서드

class ClassWithPrivateMethod {
  #privateMethod() {
    return "hello world";
  }

  getPrivateMessage() {
    return this.#privateMethod();
  }
}

const instance = new ClassWithPrivateMethod();
console.log(instance.getPrivateMessage()); // "hello worl​d"
  • 직접 접근할 수는 없지만 클래스 내부에서 접근 가능한 메서드이다.
class ClassWithPrivateAccessor {
  #message;

  get #decoratedMessage() {
    return `✨${this.#message}✨`;
  }
  set #decoratedMessage(msg) {
    this.#message = msg;
  }

  constructor() {
    this.#decoratedMessage = "hello world";
    console.log(this.#decoratedMessage);
  }
}

new ClassWithPrivateAccessor(); //`✨hello world✨`
  • private method 역시 접근자로도 사용될 수 있다.

클래스 public static 필드

class ClassWithStaticField {
  static staticField = "static field";
}

console.log(ClassWithStaticField.staticField); // "static field"​
  • public static 필드는 인스턴스로 접근하는 것이 아니라 클래스 생성자로 접근할 수 있는 필드를 말한다.
  • 모든 클래스가 같은 값을 갖는 필드 등을 캐싱해놓으면 유리하다.
class ClassWithStaticField {
  static baseStaticField = "base field";
}

class SubClassWithStaticField extends ClassWithStaticField {
  static subStaticField = "sub class field";
}

console.log(SubClassWithStaticField.subStaticField); // "sub class field"

console.log(SubClassWithStaticField.baseStaticField); // "base field"
  • public static 필드도 상속의 대상이다.
  • 다른 언어에서는 정적 필드에 대한 상속을 지원하지 않으므로 눈여겨볼만하다.

클래스 public static 메서드

class ClassWithStaticMethod {
  static baseStaticMethod() {
    return "base static method output";
  }
}

console.log(ClassWithStaticMethod.baseStaticMethod()); // base static method output
  • public static 필드 뿐만 아니라 당연히 메서드도 지원한다.
class ClassWithStaticField {
  static baseStaticField = "base static field";
  static anotherBaseStaticField = this.baseStaticField;

  static baseStaticMethod() {
    return "base static method output";
  }
}

class SubClassWithStaticField extends ClassWithStaticField {
  static subStaticField = super.baseStaticMethod();
}

console.log(ClassWithStaticField.anotherBaseStaticField); // "base static field"
console.log(SubClassWithStaticField.subStaticField); // "base static method output"
  • public static 메서드 역시 상속의 대상이다.
  • 상속 후에 super 키워드를 통해 static method 를 불러올 수도 있다.
class ClassWithStaticMethod {
  static staticProperty = "someValue";
  static staticMethod() {
    return "static method has been called.";
  }
  static {
    console.log("Class static initialization block called");
  }
}

// Class static initialization block called
console.log(ClassWithStaticMethod.staticProperty); // "someValue"
console.log(ClassWithStaticMethod.staticMethod()); // "static method has been called."
  • static 블록은 클래스가 평가되며 자동으로 실행된다.

클래스 메서드 추가

class Color {
  constructor(r = 0, g = 0, b = 0) {
    this.r = r;
    this.g = g;
    this.b = b;
  }

  toString() {
    return this.rgb;
  }
}

위는 toString() 을 추가하는 예시이다. 이전 ES5 코드에서는 아래와 같이 작성해야 했다.

Color.prototype.toString = function toString() {
  return this.rgb;
};
  • 클래스 메서드 구문이 더욱 선언적이다.
  • 새로운 구문을 이용하면 자연적으로 메서드는 enumerable 하지 않게 추가된다.
    • 이전엔 Object.defineProperty() 를 이용해야 했다.
  • 새로운 문법을 이용하면 메서드의 prototype 속성에 아무것도 존재하지 않는다.
  • 이전엔 .prototype.toString = function toString() { ... } 을 이용했으므로 자동으로 Object 를 상속하여 prototype 에 Function.prototype 이 들어있었다.

클래스 정의 방법

// 일반 정의
class Color {}

// 익명 클래스 정의
let Color = class {};

// 클래스 정의를 변수로 참조하기
let C = class Color {};
  • 총 3가지의 정의 방법이 있다.
  • 클래스 선언은 함수 선언처럼 호이스팅 되지 않는다.
  • 임시 데드존과 같이 호이스팅 된다.
  • let, const 와 마찬가지로 전역에서 선언해도 전역 객체의 속성이 아니다.

클래스 생성자의 특징

  • new 키워드를 사용해야만 생성자 호출이 가능하다.
    • 생성자 함수를 직접 호출하려하면, 오류가 발생한다.
    • 사실 Reflect 를 이용하면 호출이 가능하긴 하다.

ES5 에서 함수를 클래스처럼 이용하는 방식은 사실상 new 키워드 없이도 함수호출이 되어 혼란스러운 버그를 초래하기도 했다. this instanceof Color 와 같은 구문을 통해 예외처리를 해주어야만 했다.

Color(); // TypeError: Class constructor Color cannot be invoked without 'new'

클래스 내부 코드의 특징

  • 항상 엄격 모드("strict mode")이다.
  • 클래스 내부 속성을 정의할 때 세미콜론이나 컴마를 쓰지 않는다.
  • 내부 메서드를 선언적이고 간결하게 정의할 수 있다.
    • ES5는 Color.prototype.toString = function toString() {...} 와 같은 방식으로 정의해왔다.

이전보다 메모리 효율이 좋다.

// ES2015 `class` 키워드를 이용한 클래스와 메서드 정의
class Color {
  print() {
    console.log("color");
  }
}

var c = new Color();

console.log(typeof c.print.prototype); // undefined

// ES5 `function` 키워드를 이용한 클래스와 메서드 정의
function ColorFunction() {}
const Color2 = ColorFunction;
Color2.prototype.print = function () {
  console.log("color");
};
var c2 = new Color2();

console.log(typeof c2.print.prototype); // object
  • class 키워드를 이용한 클래스 메서드에는 prototype 이 안붙어있다.
  • function 키워드를 이용한 클래스 메서드에는 prototype 이 붙어있다.

정적 메서드 코드 살펴보기

class Color {
  constructor(r = 0, g = 0, b = 0) {
    this.r = r;
    this.g = g;
    this.b = b;
  }

  get rgb() {
    return `rgb(${this.r}, ${this.g}, ${this.b})`;
  }

  set rgb(value) {
    // TODO ...
  }

  toString() {
    return this.rgb;
  }

  static fromCSS(css) {
    const match = /^#?([0-9a-f]{3}|[0-9a-f]{6});?$/i.exec(css);

    if (!match) {
      throw new Error("invalid CSS code: " + css);
    }

    let vals = match[1];
    if (vals.length === 3) {
      vals = vals[0] + vals[0] + vals[1] + vals[1] + vals[2] + vals[2];
    }

    return new this(
      parseInt(vals.substring(0, 2), 16),
      parseInt(vals.substring(2, 4), 16),
      parseInt(vals.substring(4, 6), 16)
    );
  }
}
  • static 키워드가 붙은 메서드는 개별 인스턴스 메서드가 아닌 Color 클래스 자체의 메서드가 된다.
Color.fromCSS = function fromCSS(css) {};
  • ES5 식으로는 위와 같이 표현된다. 프로토타입 속성이 아님에 유의하자.

접근자 속성 코드 살펴보기

class Color {
  constructor(r = 0, g = 0, b = 0) {
    this.r = r;
    this.g = g;
    this.b = b;
  }

  get rgb() {
    return `rgb(${this.r}, ${this.g}, ${this.b})`;
  }

  set rgb(value) {
    let s = String(value);
    let match = /^rgb\((\d{1, 3}),(\d{1, 3}), (\d{1,3})\)$/i.exec(
      s.replace(/\s/g, "")
    );

    if (!match) {
      throw new Error(`Invalid rgb color string '${s}'`);
    }
  }

  toString() {
    return this.rgb;
  }

  static fromCSS(css) {
    const match = /^#?([0-9a-f]{3}|[0-9a-f]{6});?$/i.exec(css);

    if (!match) {
      throw new Error("invalid CSS code: " + css);
    }

    let vals = match[1];
    if (vals.length === 3) {
      vals = vals[0] + vals[0] + vals[1] + vals[1] + vals[2] + vals[2];
    }

    return new this(
      parseInt(vals.substring(0, 2), 16),
      parseInt(vals.substring(2, 4), 16),
      parseInt(vals.substring(4, 6), 16)
    );
  }
}

let c = new Color();
r.rgb = "rgb(30, 144, 255)";

ES5 에서 접근자 코드를 작성했던 방법

ES5 에서는 아래와 같이 작성해야 했다.

Object.defineProperty(Color.prototype, "rgb", {
  get: function () {
    return `${this.r}, ${this.g}, ${this.b}`;
  },
  set: function (value) {
    // ...
  },
  configurable: true,
});

static 접근자

원한다면 정적인 접근자 속성 정의도 가능하다.

class StaticAccessorExample {
  static get cappedClassName() {
    return this.name.toUpperCase();
  }
}

계산된 메서드 이름

런타임에 결정되는 이름으로 메서드를 만들고 싶을 때 유용하다.

let name = "foo" + Math.floor(Math.random() * 1000);
class SomeClass {
  [name]() {
    // ...
  }
}
  • 대괄호 안에 어떤 표현이든 넣을 수 있다.
  • 클래스 정의가 평가되는 시점에 식이 평가된다.
  • 결과가 문자열이나 심볼이 아닌 경우, 문자열로 반환된다.
  • 결과가 메서드 이름으로 사용된다.

정적 메서드

class Guide {
  static [6 * 7]() {
    // Guide["42"]() -> logging "GOOD"
    console.log("GOOD!");
  }
}

ES5

var name = "foo" + Math.floor(Math.random() * 1000);
SomeClass.prototype[name] = function () {
  // ...
};
반응형
저작자표시 비영리 (새창열림)

'자바스크립트 > 모던 자바스크립트' 카테고리의 다른 글

모던 자바스크립트, 디스트럭처링 (Desctructuring)  (0) 2023.02.08
자바스크립트 클래스 3 - 상속  (0) 2023.02.01
자바스크립트 클래스 1 - 기본 개념  (0) 2023.02.01
ES6 이후에 새롭게 정의된 Object 편의 정적 메서드들  (0) 2023.01.30
모던 자바스크립트, 메서드 정의 문법과 super, [[HomeObject]]  (0) 2023.01.25
    '자바스크립트/모던 자바스크립트' 카테고리의 다른 글
    • 모던 자바스크립트, 디스트럭처링 (Desctructuring)
    • 자바스크립트 클래스 3 - 상속
    • 자바스크립트 클래스 1 - 기본 개념
    • ES6 이후에 새롭게 정의된 Object 편의 정적 메서드들
    Jake Seo
    Jake Seo
    ✔ 잘 보셨다면 광고 한번 클릭해주시면 큰 힘이 됩니다. ✔ 댓글로 틀린 부분을 지적해주시면 기분 나빠하지 않고 수정합니다. ✔ 많은 퇴고를 거친 글이 좋은 글이 된다고 생각합니다. ✔ 간결하고 명료하게 사람들을 이해 시키는 것을 목표로 합니다.

    티스토리툴바