클래스는 객체 지향 프로그래밍에서 특정 객체를 생성하기 위해 변수와 메소드를 정의하는 템플릿 (특별한 함수)이다.
함수를 함수 표현식과 함수 선언으로 정의할 수 있듯이, 클래스 문법도 2가지 방법을 제공한다.
1. class 표현식
클래스 표현식은 이름을 가질 수도 있고, 갖지 않을 수도 있다.
표현식의 이름은 class body{ }
의 local scope에 한해 유효하다.
하지만, 클래스의 name 속성을 통해 찾을 수 있다.
// unnamed
let Rectangle = class {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
console.log(Rectangle.name);
// 출력: "Rectangle"
// named
let Rectangle = class Rectangle2 {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
console.log(Rectangle.name);
// 출력: "Rectangle2"
2. class 선언
함수 선언과 클래스 선언의 중요한 차이점은 함수의 경우 정의하기 하기 전에 호출할 수 있지만, 클래스는 반드시 정의한 뒤에 호출할 수 있다는 점이다. → Hoisting
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
class User {
constructor(name) { this.name = name; }
sayHi() { alert(this.name); }
}
// User가 함수라는 증거
alert(typeof User); // function
class User { ... }
문법 구조가 진짜 하는 일은 다음과 같다.
User
이름을 가진 함수를 만든다.
함수 본문은 생성자 메서드 constructor에서 가져온다. constructor가 없으면 본문이 비워진 채로 함수가 만들어진다.
sayHi 같은 클래스 내에서 정의한 메소드를 User.prototype
에 저장한다.
new User
를 호출해 객체를 만들고, 객체의 메소드를 호출하면 prototype
프로퍼티를 통해 메소드를 가져온다. 이 과정이 있기 때문에 객체에서 클래스 메소드에 접근할 수 있다.
지금까지 했던 설명을 코드로 표현해보자.
class User {
constructor(name) { this.name = name; }
sayHi() { alert(this.name); }
}
// 클래스는 함수입니다.
alert(typeof User); // function
// 정확히는 생성자 메서드와 동일합니다.
alert(User === User.prototype.constructor); // true
// 클래스 내부에서 정의한 메서드는 User.prototype에 저장됩니다.
alert(User.prototype.sayHi); // alert(this.name);
// 현재 프로토타입에는 메소드가 2개 입니다.
alert(Object.getOwnPropertyNames(User.prototype)); // constructor, sayHi
Class body는 중괄호 { }
로 묶인 안쪽 부분이다.
Class body에는 메소드만 선언할 수 있다. 클래스 바디에 클래스 필드를 선언하면 문법 에러가 발생한다.
클래스 필드
클래스 필드 (맴버 변수)는 클래스가 생성할 인스턴스의 프로퍼티를 가리키는 용어이다.
자바스크립트의 클래스 바디에는 메소드만 선언할 수 있다. 따라서 클래스 바디에 클래스 필드를 선언하면 문법 에러가 발생한다.class Foo { name = ''; // SyntaxError constructor() {} }
Class body는 strict mode
에서 실행된다.
constructor
메소드는 인스턴스를 생성하고 초기화하기 위한 특수한 메소드이다. 이를 사용하면 다른 모든 메소드 호출보다 앞선 시점인, 인스턴스 객체를 초기화할 때 수행할 초기화 코드를 정의할 수 있다.
이는 클래스 안에 한 개만 존재할 수 있다.
class Rectangle {
constructor(height, width) {
// 인스턴스 속성
this.height = height;
this.width = width;
}
// 프로토타입 메소드
calcArea() {
return this.height * this.width;
}
}
const square = new Rectangle(10, 10);
console.log(square.calcArea()); // 100
static
키워드는 클래스를 위한 정적 메소드를 정의한다. 정적 메소드는 클래스의 인스턴스를 생성하지 않아도 호출이 가능하며 클래스가 인스턴스화되면 호출할 수 없다.
→ ❗이것은 정적 메소드는
this
를 사용할 수 없다는 것을 의미한다. 일반 메소드 내부에서 this는 클래스의 인스턴스를 가리키며, 메소드 내부에서 this를 사용한다는 것은 클래스의 인스턴스의 생성을 전제로 하는 것이다.
정적 메소드는 클래스로 호출되기 때문에, 인스턴스로 호출되는 클래스와는 내부의this
가 다를 수 밖에 없다. 따라서,this
를 사용해야하는 경우엔 프로토타입 메소드로 정의해야 한다. 반면에this
로 인스턴스의 프로퍼티를 참조해야할 필요가 없고 클래스 호출만으로도 충분하다면 정적 메소드로 만들면 된다.
class ClassWithStaticMethod {
static staticProperty = 'someValue';
static staticMethod() {
return 'static method has been called.';
}
static {
console.log('Class static initialization block called');
}
}
console.log(ClassWithStaticMethod.staticProperty);
// Expected output: "someValue"
console.log(ClassWithStaticMethod.staticMethod());
// Expected output: "static method has been called."
class Foo {
constructor(prop) {
this.prop = prop;
}
static staticMethod() {
/*
정적 메소드는 this를 사용할 수 없다.
정적 메소드 내부에서 this는 클래스의 인스턴스가 아닌 클래스 자신을 가리킨다.
*/
return 'staticMethod';
}
prototypeMethod() {
return this.prop;
}
}
// 정적 메소드는 클래스 이름으로 호출한다.
console.log(Foo.staticMethod());
const foo = new Foo(123);
// 정적 메소드는 인스턴스로 호출할 수 없다.
console.log(foo.staticMethod()); // Uncaught TypeError: foo.staticMethod is not a function