1. 생성자 함수와 클래스
생성자 함수와 클래스는 모두 프로토타입 기반의 인스턴스를 생성하지만 똑같이 동작하지는 않는다. 클래스는 생성자 함수 보다 엄격하고 새로운 기능도 제공한다.
생성자 함수와 클래스의 차이점
생성자 함수 | 클래스 |
new 연산자 없이 호출하면 일반 함수로서 호출 됨 | new 연산자 없이 호출하면 에러가 발생 함 |
extends와 super 키워드를 지원하지 않음 | 상속을 지원하는 extends와 super 키워드를 지원 함 |
함수 호이스팅, 변수 호이스팅 | 호이스팅이 발생하지 않는 것처럼 동작 |
암묵적으로 strict mode 지정 안됨 | 클래스 내 모든 코드에 암묵적으로 strict mode가 지정 됨 |
클래스의 constructor, 프로토타입 메서드, 정적 메서드는 모두 프로퍼티 어트리뷰트 [[Enumerable]]의 값이 false 이므로 열거되지 않는다. |
2. 클래스 정의
- 클래스는 Class 키워드를 사용하여 정의한다.
- 이름은 파스칼케이스를 사용하는 것이 일반적이다. (사용하지 않아도 에러가 나지는 않는다)
- 클래스는 표현식으로도 정의할 수 있다. 즉, 값으로 사용할 수 있는 일급 객체라는 것을 의미한다.
- 클래스는 일급 객체로서 특징을 갖는다.
- 무명 리터럴로 생성 가능, 즉 런타임에 생성이 가능
- 변수나 자료구조(객체, 배열 등)에 저장 가능
- 함수의 매개변수에개 전달 가능
- 함수의 반환값으로 사용 가능
- 클래스에는 0개 이상의 메서드만 정의 할 수 있다.
- constructor(생성자)
- 프로토타입 메서드
- 정적 메서드
// 클래스 선언문
class Person {
// 생성자
constructor(name) {
// 인스턴스 생성 및 초기화
this.name = name;
}
// 프로토타입 메서드
sayHi() {
console.log(`Hi! My name is ${this.name} );
}
// 정적 메서드
static sayHello() {
console.log('Hello!');
}
}
// 인스턴스 생성
const me = new Person('Lee');
// 인스턴스의 프로퍼티 참조
console.log(me.name); // Lee
// 프로토타입 메서드 호출
me.sayHi(); // Hi! My name is Lee
// 정적 메서드 호출
Person.sayHello(); // Hello!
💡 클래스와 생성자 함수 비교
💡 클래스는 호이스팅 될까?
클래스도 변수 선언, 함수 정의와 마찬가지로 호이스팅이 발생한다.
단, 클래스는 let, consr 키워드로 선언한 변수처럼 호이스팅 된다. 클래스 선언문 이전에 일시적 사각지대(TDZ)에 빠지기 때문에 호이스팅이 발생하지 않는 것처럼 동작한다.
3. 인스턴스 생성
class Person { }
// 인스턴스 생성
const me = new Person();
console.log(me); // person { }
- new 연산자와 함께 호출되어 인스턴스를 생성한다.
- 클래스는 인스턴스를 생성하는 것이 유일한 존재 이유이므로 반드시 new 연산자와 함께 호출해야한다.
- 표현식으로 정의된 클래스는 식별자를 사용해 인스턴스를 생성하지 않고 클래스 이름을 사용해 인스턴스를 생성하면 에러가 발생한다.
const Person = class MyClass { };
const me = new Person();
const you = new MyClass(); // ReferenceError
4. 메서드
클래스에는 0개 이상의 메서드만 선언할 수 있는데 costructor(생성자), 프로토타입 메서드, 정적 메서드를 갖는다.
4.1 costructor(생성자)
- 인스턴스를 생성 및 초기화 하는 특수한 메서드이다.
- 이름을 변경할 수 없다.
- costructor 내부의 this는 클래스가 생성한 인스턴스를 가리킨다.
- 클래스 내에 최대 한 개만 존재한다.
- costructor 생략 가능 하지만 생략하면 빈 costructor가 암묵적으로 정의 된다.
- return문을 생략해야 한다. (this가 아닌 다른 객체를 반환하면 인스턴스가 반환되지 않고 다른 객체가 반환되기 때문)
class Person {
constructor() {
// 고정값으로 인스턴스 초기화
this.name = 'Lee'
}
}
// 인스턴스 프로퍼티 추가
const me = new Person();
console.log(me); // { name: 'Lee' }
- 프로퍼티가 추가되어 초기화된 인스턴스를 생성하려면 costructor 내부에서 this에 인스턴스 프로퍼티를 추가한다.
class Person {
constructor(name) {
// 인수로 인스턴스 초기화
this.name = name;
}
}
// 인수로 초기 값 전달
const me = new Person('Lee');
console.log(me); // { name: 'Lee' }
- 인스턴스를 생성할 때 클래스 외부에서 인스턴스 프로퍼티의 초기값을 전달하려면 constructor에 매개변수를 선언하고 인스턴스를 생성할 때 초기값을 전달한다.
- 이처럼 초기화하려면 constructor를 생략하면 안된다.
4.2 프로토타입 메서드
// 프로토타입 메서드
sayHi() {
console.log(`Hi! My name is ${this.name} );
}
const me = new Person('Lee');
me.sayHi(); // Hi! My name is Lee
- 클래스는 생성자 함수와 다르게 prototype 프로퍼티에 메서드를 추가하지 않아도 기본적으로 프로토타입 메서드가 된다.
- 클래스가 생성한 인스턴스는 프로토타입 체인이 된다.
- 인스턴스는 프로토타입 메서드를 상속받아 사용할 수 있다.
4.3 정적 메서드
// 정적 메서드
static sayHello() {
console.log('Hello!');
}
}
Person.sayHello(); // Hello!
- 인스턴스를 생성하지 않아도 호출할 수 있는 메서드
- static 키워드를 붙이면 된다.
- 정적메서드는 프로토타입 메서드처럼 인스턴스로 호출하지 않고 클래스로 호출한다.
- 즉, 인스턴스로 호출할 수 없다.
4.4 정적 메서드와 프로토타입 메서드의 차이
1) 자신이 속해 있는 프로토타입 체인이 다름
2) 정적 메서드는 클래스로 호출하고 프로토타입 메서드는 인스턴스로 호출함
3) 정적 메서드는 인스턴스 프로퍼티를 참조할 수 없지만 프로토타입 메서드는 참조할 수 있음
class Square {
// 정적 메서드
static area(width, height) {
return width * heigh;
}
}
console.log(Square.area(10,10)); // 100
- 정적 메서드는 인스턴스 프로퍼티를 참조하지 않는다.
- 정적 메서드 내부의 this는 인스턴스가 아닌 클래스를 가리킨다.
class Square {
constructor(width, height) {
this.width = width;
this.height = height;
}
// 프로토타입 메서드
area() {
return this.width * this.height;
}
}
const square = new Square(10,10);
console.log(square.area()); // 100
- 메서드 내부의 this는 메서드를 소유한 객체가 아닌 메서드를 호출한 객체, 즉 메서드 이름 앞의 마침표(.) 연산자 앞에 기술한 객체에 바인딩된다.
- 프로토타입 메서드 내부의 this는 메서드를 호출한 인스턴스를 가리킨다.
💡결론적으로 프로토타입 메서드와 정적 메서드는 내부의 this 바인딩이 다르다.
5. 클래스의 인스턴스 생성과정
class Person {
constructor(name) {
//1. 암묵적으로 인스턴스가 생성되고 this에 바인딩된다.
console.log(this); // Person { }
console.log(Object.getPrototypeOf(this) === Person.prototype); // true
//2.this에 바인딩되어 있는 인스턴스를 초기화한다.
this.name = name;
//3.완성된 인스턴스가 바인딩된 this가 암묵적으로 반환된다.
}
}
1) 인스턴스 생성과 this 바인딩
new 연산자와 함께 클래스를 호출하면 constructor의 코드가 실행되기 전 암묵적으로 빈 객체(인스턴스)가 생성되는데 이 인스턴스는 this에 바인딩 된다. 즉 this는 클래스가 생성한 인스턴스를 가리킨다.
2) 인스턴스 초기화
constructor의 코드가 실행되어 this에 바인딩 되어 있는 인스턴스를 초기화 한다. 만약 constructor가 생략되어 있다면 이 과정도 생략된다.
3) 인스턴스 반환
클래스의 모든 처리가 끝나면 완성된 인스턴스가 바인딩된 this를 암묵적으로 반환한다.
6. 클래스의 상속과 확장
프로토타입 기반의 상속은 프로토타입 체인을 통해 다른 객체의 자산을 상속받는 개념이지만 상속에 의한 클래스 확장은 기존 클래스를 상속받아 새로운 클래스를 확장(extends)하여 정의하는 것이다.
- 상속에 의한 클래스 확장은 코드 재사용 관점에서 매우 유용하다.
- extends 키워드를 사용하여 상속받을 클래스를 정의하고 확장할 수 있다.
- super 키워드를 사용해서 함수처럼 호출도 할 수 있으며 this와 같이 식별자처럼 참조할 수도 있다.
📌 출처
모던 자바스크립트 Deep Dive(이웅모 저)
'Front-end > 모던자바스크립트' 카테고리의 다른 글
[JS] Ajax와 REST API (0) | 2023.12.20 |
---|---|
[JS] 비동기 (1) | 2023.12.17 |
[JS] 프로토타입 (0) | 2023.12.15 |
[JS] 생성자 함수 (0) | 2023.12.15 |
[JS] this (0) | 2023.12.13 |