반응형
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)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

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

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
Jake Seo

제이크서 위키 블로그

자바스크립트/개념

자바스크립트 클래스 간단 정리

2022. 7. 30. 17:05

개요

클래스는 오브젝트를 만들기 위한 템플릿이다. 데이터와 데이터를 조작하는 코드를 하나로 추상화한다. JS 클래스는 프로토타입을 기반으로 빌드되었지만 ES5 의 클래스 같은 의미와는 다른 문법과 의미를 가진다.

클래스 정의

JS 의 '특별한 함수'이다. JS 에서는 모든 것이 오브젝트로 표현 가능하다는 점을 염두에 두자.

구조

클래스 문법에는 2가지 구성 요소가 있다.

  • 클래스 선언 (Class Declaration)
  • 클래스 표현식 (Class Expression)

클래스 선언 (Class Declaration) 알아보기

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

호이스팅

const p = new Rectangle(); // ReferenceError

class Rectangle {
  // ...
}

클래스 선언과 함수 선언의 차이는 함수는 선언된 곳 이전부터 사용이 가능한데, 클래스는 선언 코드가 작동한 이후에 new 키워드를 이용해 생성되어야 한다는 것이다. 위의 코드는 ReferenceError 가 나는 예제이다.

호이스팅의 기본 원리를 알고 있다면 어렵지 않게 이해할 수 있는 부분이다.

클래스 표현식 (Class Expression)

클래스 표현식은 named 와 unnamed로 나뉜다.

자바스크립트에서 표현식 이라는 개념은 보통 변수에 특정한 표현을 통해 해당 객체를 넣는 것을 말한다. 비슷한 예로 함수 표현식 이 있다.

// unnamed
let Rectangle = class {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};
console.log(Rectangle.name);
// output: "Rectangle"

// named
Rectangle = class Rectangle2 {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};
console.log(Rectangle.name);
// output: "Rectangle2"

named 와 unnamed 의 차이는 클래스 내부의 name 프로퍼티에 값을 변수명과 다르게 입력할 것인지 정도의 차이가 있다.

클래스 바디와 메서드 정의 알아보기

클래스 바디는 {} 내부에 들어간다. 내부에 클래스 멤버인 메서드, 생성자를 정의한다.

Strict mode

클래스 바디는 더 나은 성능을 제공하기 위해 Strict mode 에서 실행된다. 문법을 따르지 않으면 silent errors 가 던져질 것이다. 그리고 몇몇 키워드는 미래의 ECMA Script 를 위해 예약되어 있다.

Constructor

생성자(constructor) 메서드는 class 로 생성된 객체를 생성하고 초기화하기 위한 특수한 메서드이다. 클래스 안에 한개만 존재해야 하고, 그 이상 존재한다면 SyntaxError 가 발생한다.

생성자 내부에서는 super 키워드를 통해 부모의 생성자를 불러오는 것도 가능하다.

정적 초기화 블럭 (Static initialize blocks)

정적 초기화 블럭은 클래스 정적 프로퍼티 를 유연하게 초기화할 수 있게 해준다. 초기화 동안의 구문의 평가와 private scope 접근 권한까지 포함한다.

여러 개의 정적 초기화 블럭이 선언될 수 있으며, 정적 프로퍼티 메서드와 함께 교차되는 것도 가능하다. 모든 정적 아이템은 선언 순서대로 평가된다.

프로토타입 메서드

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }

  // Getter
  get area() {
    return this.calcArea();
  }

  // Method
  calcArea() {
    return this.height * this.width;
  }
}

const square = new Rectangle(10, 10);

console.log(square.area); // 100

제너레이터 메서드

Iterators and generators 페이지에서도 볼 수 있다.

class Polygon {
  constructor(...sides) {
    this.sides = sides;
  }

  // Method
  *getSides() {
    for (const side of this.sides) {
      yield side;
    }
  }
}

const pentagon = new Polygon(1, 2, 3, 4, 5);

console.log([...pentagon.getSides()]); // [1, 2, 3, 4, 5]

정적 메서드와 프로퍼티

static 키워드로 선언된 메서드는 인스턴스에서는 사용할 수 없고 클래스에서 유틸 메서드처럼 접근해 사용하는 것이 권장된다. 캐시, 고정 환경 설정 혹은 인스턴스 생성 시마다 매번 만들면 중복되는 데이터 등을 관리할 수 있다.

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  static displayName = "Point";
  static distance(a, b) {
    const dx = a.x - b.x;
    const dy = a.y - b.y;

    return Math.hypot(dx, dy);
  }
}

const p1 = new Point(5, 5);
const p2 = new Point(10, 10);

p1.displayName; // undefined;
p2.displayName; // undefined; -> 인스턴스에서 접근 불가

p1.distance; // undefined;
p2.distance; // undefined; -> 인스턴스에서 접근 불가

console.log(Point.displayName); // "Point" -> 클래스에서 접근 가능
console.log(Point.distance(p1, p2)); // 7.0710678118654755

프로토타입과 정적메서드와 함께 this 바인딩하기

this 의 값을 클래스 바디에서 불러오게 되면, 기본적으로 undefined 가 반환된다. 그 이유는 클래스 바디에서 "use strict" 가 이미 적용되어 있기 때문이다. 아래의 예제코드를 콘솔에서 실행해보면 쉽게 알 수 있다. this 의 바인딩은 strict 인지 아닌지에 따라 달라진다.

function strict() {
  "use strict";
  console.log(this);
}

function nonStrict() {
  console.log(this);
}
class Animal {
  speak() {
    return this;
  }
  static eat() {
    return this;
  }
}

const obj = new Animal();
obj.speak(); // the Animal object
const speak = obj.speak;
speak(); // undefined

Animal.eat(); // class Animal
const eat = Animal.eat;
eat(); // undefined

아래와 같이 non-strict 모드를 사용하도록 하는 방식으로 prototype 이나 static method 를 설정하면 this 를 바인딩할 수 있다.

function Animal() {}

Animal.prototype.speak = function () {
  return this;
};

Animal.eat = function () {
  return this;
};

const obj = new Animal();
const speak = obj.speak;
speak(); // global object (in non–strict mode)

const eat = Animal.eat;
eat(); // global object (in non-strict mode)

인스턴스 프로퍼티

인스턴스 프로퍼티는 무조건 클래스 메서드 안에서 정의되어야 한다.

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

필드 선언

퍼블릭 필드 선언

  • let, const, var 키워드는 필요 없다.
  • 맨 위쪽(up-front)에 먼저 선언해둬야 self-documenting 의 효과를 볼 수 있다.
  • 아래의 예제처럼 기본 값을 설정해둘 수도 있다.

퍼블릭 필드 관련 공식문서

class Rectangle {
  height = 0;
  width;
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

프라이빗 필드 선언

  • 클래스 밖에서 프라이빗 필드를 이용하면 참조 에러가 발생한다.
  • 클래스 바디에서만 읽거나 쓰여질 수 있다.
  • 프라이빗 필드는 일반 필드처럼 나중에 프로퍼티를 이용하여 만들어낼 수 없다.
  • 클래스 내부에서 구성하였으므로 이용자는 버전에 따라 바뀔 수 있는 내부 구현에 의존하지 않는다는 것을 확신할 수 있다.

프라이빗 필드 관련 공식문서

class Rectangle {
  #height = 0;
  #width;
  constructor(height, width) {
    this.#height = height;
    this.#width = width;
  }
}

extends 키워드를 이용한 클래스 상속 (Sub classing)

  • 클래스를 다른 클래스의 자식으로 만들기 위해 클래스 선언, 혹은 클래스 표현식에서 사용된다.
  • 생성자에서 super() 메서드를 이용해 부모의 생성자를 호출해야 한다.

클래스 상속 예제

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} makes a noise.`);
  }
}

class Dog extends Animal {
  constructor(name) {
    super(name); // call the super class constructor and pass in the name parameter
  }

  speak() {
    console.log(`${this.name} barks.`);
  }
}

const d = new Dog("Mitzie");
d.speak(); // Mitzie barks.

함수를 기반으로 한 클래스 상속 예제

위와 비슷하나,

  • 부모 클래스를 생성할 때 prototype 으로 공통 메서드를 지정한다.
  • constructor 키워드를 이용해 생성자를 만들지 않는다.
function Animal(name) {
  this.name = name;
}

Animal.prototype.speak = function () {
  console.log(`${this.name} makes a noise.`);
};

class Dog extends Animal {
  speak() {
    console.log(`${this.name} barks.`);
  }
}

const d = new Dog("Mitzie");
d.speak(); // Mitzie barks.

// For similar methods, the child's method takes precedence over parent's method

생성자가 없는 일반 함수를 상속하기

생성자가 없는 일반 함수를 상속할 때는 Object.setPrototypeOf() 메서드를 이용하면 가능하다.

const Animal = {
  speak() {
    console.log(`${this.name} makes a noise.`);
  },
};

class Dog {
  constructor(name) {
    this.name = name;
  }
}

// If you do not do this you will get a TypeError when you invoke speak
Object.setPrototypeOf(Dog.prototype, Animal);

const d = new Dog("Mitzie");
d.speak(); // Mitzie makes a noise.

Species

직접 만든 클래스에서 Array 를 상속받아 기본 생성자가 실행된 이후에는 Array 타입을 반환할 수 있다.

class MyArray extends Array {
  // 부모 Array 생성자로 species 덮어쓰기
  static get [Symbol.species]() {
    return Array;
  }
}
var a = new MyArray(1, 2, 3);
var mapped = a.map((x) => x * x);

console.log(mapped instanceof MyArray); // false
console.log(mapped instanceof Array); // true

static get [Symbol.species]() { return Array; } 코드가 없었다면, 둘 다 true 가 나왔을 것이다.

super 를 통한 상위 클래스 호출

객체의 부모가 가진 메서드를 호출할 수 있다. 이는 프로토타입 기반 상속에 대비되는 장점 중 하나이다. 부모의 메서드와 자신의 메서드 중 선택할 수 있다.

class Cat {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} makes a noise.`);
  }
}

class Lion extends Cat {
  speak() {
    super.speak();
    console.log(`${this.name} roars.`);
  }
}

let l = new Lion("Fuzzy");
l.speak();
// Fuzzy makes a noise.
// Fuzzy roars.

믹스인 (Mix-ins)

추상 서브 클래스 혹은 믹스인이라고 불리는 이것은 클래스를 위한 템플릿이다. ECMAScript 클래스는 하나의 상위 클래스만 가질 수 있다.

슈퍼클래스를 인자로 받고 이 슈퍼클래스를 확장하는 서브 클래스를 생성하여 반환하는 함수를 구현하는 방식으로 ECMAScript 에서 믹스인(Mix-ins)을 구현할 수 있다.

var calculatorMixin = (Base) =>
  class extends Base {
    calc() {}
  };

var randomizerMixin = (Base) =>
  class extends Base {
    randomize() {}
  };

위 코드의 Base 에 클래스를 넣으면 된다.

class Foo {}
class Bar extends calculatorMixin(randomizerMixin(Foo)) {}

클래스 재정의

클래스 재정의는 불가능하며, 재정의를 시도하면 SyntaxError 가 발생한다.

레퍼런스

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes

반응형
저작자표시 비영리 (새창열림)

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

오브젝트 리터럴 ('{}') 이 new Object() 보다 빠른 이유  (0) 2022.12.22
Array.prototype.sort() 자세히 알아보기  (0) 2022.12.20
자바스크립트 호이스팅 매우 간단히 정리  (0) 2022.07.30
자바스크립트 DOMContentLoaded vs load (onload) 의 차이  (0) 2022.07.24
자바스크립트 클로저 (Closure) 란 무엇인가?  (0) 2022.07.13
    '자바스크립트/개념' 카테고리의 다른 글
    • 오브젝트 리터럴 ('{}') 이 new Object() 보다 빠른 이유
    • Array.prototype.sort() 자세히 알아보기
    • 자바스크립트 호이스팅 매우 간단히 정리
    • 자바스크립트 DOMContentLoaded vs load (onload) 의 차이
    Jake Seo
    Jake Seo
    ✔ 잘 보셨다면 광고 한번 클릭해주시면 큰 힘이 됩니다. ✔ 댓글로 틀린 부분을 지적해주시면 기분 나빠하지 않고 수정합니다. ✔ 많은 퇴고를 거친 글이 좋은 글이 된다고 생각합니다. ✔ 간결하고 명료하게 사람들을 이해 시키는 것을 목표로 합니다.

    티스토리툴바