일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
- Algorithm
- 버블정렬
- TypeScript
- styled-components
- 큐
- 그리디
- tailwindcss
- nextjs
- typscript
- JavaScript
- 스택
- next.js
- textarea autosize
- react-query
- 슬라이딩윈도우
- 정렬
- 블로그만들기
- 빅오
- never타입
- js알고리즘
- 알고리즘
- 투포인터
- 라이프사이클
- aws lightsail
- 해쉬
- react
- isNaN
- nestjs
- cookie
- NextAuth
- Today
- Total
far
[Javascript] 클래스 문법 본문
(프로토타입 기반 객체지향 모델을 폐지하고 클래스 기반 객체지향 모델을 제공하는 것은 아님)
생성자 함수와의 유사패턴
1. 클래스를 new 연산자 없이 호출하면 에러가 발생한다. 하지만 생성자 함수를 new 연산자 없이 호출하면 일반 함수로서 호출된다.
2. 클래스는 상속을 지원하는 extends와 super키워드를 제공한다. 생성자 함수는 X
3. 클래스는 호이스팅이 발생하지 않는 것처럼 동작한다. 하지만 함수 선언문으로 정의된 생성자 함수는 함수 호이스팅이, 함수 표현식으로 정의한 생성자 함수는 변수 호이스팅이 발생한다.
4.클래스 내의 모든 코드에는 암묵적으로 strict mode가 지정되어 실행되며 strict mode를 해제할 수 없다. 하지만 생성자 함수는 X
5. 클래스의 constructor, 프로토타입 메서드, 정적 메서드는 모두 프로퍼티 어트리뷰트 [[Enumerable]]의 값이 false다. 다시 말해 열거되지 않는다.
클래스는 표현식으로 정의가능 (일반적이지는 X)
클래스는 함수로 평가된다. 런타임 이전에 먼저 평가되어 함수 객체를 생성한다. 생성자 함수로서 호출할 수 있는 함수는 함수 정의가 평가되어 함수 객체를 생성하는 시점에 프로토타입도 더불어 생성된다.
단, 클래스 정의 이전에 참조 불가능
호이스팅이 let, const처럼 발생한다.
constructor
이름 변경이 불가능
클래스가 평가되어 생성한 함수 객체 코드의 일부가 된다. 즉, 클래스 정의가 평가되면 constructor의 기술된 동작을 하는 함수 객체가 생성된다.
한 클래스에 하나, 생략 가능, 하지만 인스턴스를 초기화 하려면 constructor를 초기화해서는 안된다.
정적 메서드와 프로토타입 메서드의 차이
1. 정적 메서드와 프로토타입 메서드는 자신이 속해 있는 프로토타입 체인이 다르다.
2. 정적 메서드는 클래스로 호출하고 프로토타입 메서드는 인스턴스로 호출한다.
3. 정적 메서드는 인스턴스 프로퍼티를 참조할 수 없지만 프로토타입 메서드는 인스턴스 프로퍼티를 참조할 수 있다.
class Square {
static area(width, height) {
return width * height;
}
}
console.log(Square.area(10, 10)); // 100
정적 메서드 area는 인스턴스 프로퍼티를 참조하지 않는다. 만약 참조해야 한다면 프로토타입 메서드를 사용해야 한다.
메서드 내부의 this는 메서드를 소유한 객체가 아니라 메서드를 호출한 객체, 즉 메서드 이름 앞의 마침표(.) 연산자 앞에 기술한 객체에 바인딩 된다.
정적 메서드는 클래스로 호출해야 하므로 정적 메서드 내부의 this는 인스턴스가 아닌 클래스를 가리킨다. 즉, 프로토타입 메서드와 정적 메서드 내부의 this 바인딩이 다르다.
따라서 내부에서 인스턴스 프로퍼티를 참조할 필요가 있다면 프로토타입 메서드로 정의 후 this를 사용해야 한다.
물론 this를 사용하지 않더라도 프로토타입 메서드로 정의할 수 있다. 하지만 반드시 인스턴스를 생성한 다음 호출해야 하므로 this를 사용하지 않는다면 정적 메서드로 정의하는 것이 좋다.
클래스에서 정의한 메서드의 특징
1. function 키워드를 생략한 메서드 축약 표현을 사용한다.
2. 객체 리터럴과는 다르게 클래스에 메서드를 정의할 때는 콤마가 필요 없다.
3. 암묵적으로 strict mode로 실행된다.
4. for ... in 문이나 Object.keys 메서드 등으로 열거할 수 없다. 즉, 프로퍼티의 열거 가능 여부를 나타내며, 불리언 값을 갖는 프로퍼티 어트리뷰트 [[Enumerable]]의 값이 false다.
5. 내부 메서드 [[Construct]]를 갖지 않는 non-constructor다. 따라서 enw 연산자와 함께 호출할 수 없다.
private 필드 정의 제안
JS 클래스는 private, public, protected 같은 접근 제한자를 지원하지 않는다.
하지만 TC39 프로세스의 stage 3(candidate)에는 private를 정의할 수 있는 새로운 표준 사양이 제안되었다.
class Person {
// private 필드 정의
#name = '';
constructor(name) {
// private 필드 참조
this.#name = name;
}
}
const me = new Person('Lee');
console.log(me.#name); // SyntaxError: Private field '#name' must be declared in an enclosing class
private필드의 선두에는 #를 붙여준다.
static 키워드 역시 TC39부터 제안되었다.
class MyMath {
// static public 필드 정의
static PI = 22 / 7;
// static private 필드 정의
static #num = 10;
// static 메서드
static increment() {
return ++MyMath.#num;
}
}
console.log(MyMath.PI); // 3.142857142857143
console.log(MyMath.increment()); // 11
super
super를 호출하면 수퍼클래스의 constructor(super-constructor)를 호출한다.
다음 예제와 같이 수퍼클래스에서 추가한 프로퍼티와 서브클래스에서 추가한 프로퍼티를 갖는 인스턴스를 생성한다면 서브클래스의 constructor를 생략할 수 없다. 이때 new 연산자와 함께 서브클래스를 호출하면서 전달한 인수 중에서 수퍼클래스의 constructor에 전달할 필요가 있는 인수는 서브클래스의 constructor에서 호출하는 super를 통해 전달한다.
class Base {
constructor(a, b) { // ➃
this.a = a;
this.b = b;
}
}
class Derived extends Base {
constructor(a, b, c) { // ➁
super(a, b); // ➂
this.c = c;
}
}
const derived = new Derived(1, 2, 3); // ➀
console.log(derived); // Derived {a: 1, b: 2, c: 3}
new 연산자와 함께 Derived 클래스를 호출(➀)하면서 전달한 인수 1, 2, 3은 Derived 클래스의 constructor(➁)에 전달되고 super 호출(➂)을 통해 Base 클래스의 constructor(➃)에 일부가 전달된다.
super를 호출할 때 주의할 사항은 다음과 같다.
서브클래스에서 constructor를 생략하지 않는 경우 서브클래스의 constructor에서는 반드시 super를 호출해야 한다.
class Base {}
class Derived extends Base {
constructor() {
// ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
console.log('constructor call');
}
}
const derived = new Derived();
서브클래스의 constructor에서 super를 호출하기 전에는 this를 참조할 수 없다.
class Base {}
class Derived extends Base {
constructor() {
// ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
this.a = 1;
super();
}
}
const derived = new Derived(1);
super는 반드시 서브클래스의 constructor에서만 호출한다. 서브클래스가 아닌 클래스의 constructor나 함수에서 super를 호출하면 에러가 발생한다.
class Base {
constructor() {
super(); // SyntaxError: 'super' keyword unexpected here
}
}
function Foo() {
super(); // SyntaxError: 'super' keyword unexpected here
}
super 참조
메서드 내에서 super를 참조하면 수퍼클래스의 메서드를 호출할 수 있다.
서브클래스의 프로토타입 메서드 내에서 super.sayHi는 수퍼클래스의 프로토타입 메서드 sayHi를 가리킨다.
class Base {
constructor(name) {
this.name = name;
}
sayHi() {
return `Hi ${this.name}`;
}
}
class Derived extends Base {
sayHi() {
// super.sayHi는 수퍼클래스의 프로토타입 메서드를 가리킨다.
return `${super.sayHi()}. how are you doing?`;
}
}
const derived = new Derived('Lee');
console.log(derived.sayHi()); // Hi Lee. how are you doing?
super 참조를 통해 수퍼클래스의 메서드는 참조하려면 super가 수퍼클래스의 메서드가 바인딩된 객체, 즉 수퍼클래스의 prototype 프로퍼티에 바인딩된 프로토타입을 참조할 수 있어야 한다. 위 예제는 다음 예제와 동일하게 동작한다.
서브클래스의 정적 메서드 내에서 super.sayHi는 수퍼클래스의 정적 메서드 sayHi를 가리킨다.
class Base {
static sayHi() {
return 'Hi!';
}
}
class Derived extends Base {
static sayHi() {
// super.sayHi는 수퍼클래스의 정적 메서드를 가리킨다.
return `${super.sayHi()} how are you doing?`;
}
}
console.log(Derived.sayHi()); // Hi how are you doing?
상속 클래스의 인스턴스 생성과정
클래스가 단독으로 인스턴스를 생성하는 과정보다 상속 관계에 있는 두 클래스가 협력하여 인스턴스를 생성하는 과정은 좀 더 복잡하다.
다음의 Rectangle 클래스와 상속받은 ColorRectangle 클래스를 보자.
class Rectangle {
constructor(width, height) {
this.width = width;
this.height = height;
}
getArea() {
return this.width * this.height;
}
toString() {
return `width = ${this.width}, height = ${this.height}`;
}
}
class ColorReactangle extends Rectangle {
constructor(width, height, color) {
super(width, height);
this.color = color;
}
// 메서드 오버라이딩
toString() {
return super.toString() + `, color = ${this.color}`;
}
}
const colorReactangle = new ColorReactangle(2, 4, 'red');
console.log(colorReactangle); // ColorRectangle {width: 2, height: 4, color: "red"}
console.log(colorReactangle.getArea()); // 8
console.log(colorReactangle.toString()); // width = 2, height = 4, color = red
super클래스는 책 한번 더 볼 것
'Javascript' 카테고리의 다른 글
[Javascript] textarea Autosize 만들기 (스크롤이 자동으로 올라가는 현상 해결하기) (0) | 2023.04.24 |
---|---|
[Javascript] 연속된 클릭 막기 (0) | 2023.04.06 |
[Javascript] 외부 클릭시 태그 닫기 + 커스텀 셀렉트 (0) | 2023.04.05 |
[Javascript] 클로저 (0) | 2023.03.30 |
[Javascript] innerHTML, innerText, textContent 차이 (0) | 2023.03.30 |