클래스
: 객체지향 프로그래밍에서 특정 객체를 생성하기 위해 변수와 메소드를 정의하는 일종의 틀로
객체를 정의하기 위한 상태인 멤버 변수와 메서드로 구성
class MyClass {
// 여러 메서드를 정의할 수 있음
constructor() { ... }
method1() { ... }
method2() { ... }
method3() { ... }
...
}
class User {
constructor(name) {
this.name = name;
}
sayHi() {
alert(this.name);
}
}
// 사용법:
let user = new User("John");
user.sayHi();
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);
// 현재 프로토타입에는 메서드가 두 개입니다.
alert(Object.getOwnPropertyNames(User.prototype)); // constructor, sayHi
클래스 = 편의문법 (syntatic sugar)?
: class 키워드 없이 클래스 역할을 하는 함수를 선언할 수 있기 때문
- 차이점
: class로 만든 하뭇에는 특수 내부 프로퍼티인 [[IsClassConstructor]]:true 가 붙음
> 클래스 생성자를 new와 함께 호출하지 않으면 에러가 발생하는 이유
: 클래스에 정의 된 메서드는 열거할 수 없음
: 클래스는 항상 엄격모드로 실행 됨
클래스 표현식
// 기명 클래스 표현식(Named Class Expression)
// (명세서엔 없는 용어이지만, 기명 함수 표현식과 유사하게 동작합니다.)
let User = class MyClass {
sayHi() {
alert(MyClass); // MyClass라는 이름은 오직 클래스 안에서만 사용할 수 있습니다.
}
};
new User().sayHi(); // 원하는대로 MyClass의 정의를 보여줍니다.
alert(MyClass); // ReferenceError: MyClass is not defined, MyClass는 클래스 밖에서 사용할 수 없습니다.
: 기명 함수 표현식
function makeClass(phrase) {
// 클래스를 선언하고 이를 반환함
return class {
sayHi() {
alert(phrase);
};
};
}
// 새로운 클래스를 만듦
let User = makeClass("안녕하세요.");
new User().sayHi(); // 안녕하세요
: 동적인 생성
클래스와 getter와 setter
class User {
constructor(name) {
// setter를 활성화합니다.
this.name = name;
}
get name() {
return this._name;
}
set name(value) {
if (value.length < 4) {
alert("이름이 너무 짧습니다.");
return;
}
this._name = value;
}
}
let user = new User("보라");
alert(user.name); // 보라
user = new User(""); // 이름이 너무 짧습니다.
계산된 메서드 이름 [...]
class User {
name = "보라";
}
let user = new User();
alert(user.name); // 보라
alert(User.prototype.name); // undefined
class User {
name = prompt("이름을 알려주세요.", "보라");
}
let user = new User();
alert(user.name); // 보라
클래스 필드로 바인딩 된 메서드 만들기
class Button {
constructor(value) {
this.value = value;
}
click = () => {
alert(this.value);
}
}
let button = new Button("안녕하세요.");
setTimeout(button.click, 1000); // 안녕하세요.
클래스 상속
: 클래스를 다른 클래스로 확장할 수 있는 방법
: 기존에 존재하던 기능을 토대로 새로운 기능을 만들 수 있음
- extends 키워드
: 클래스를 상속 받기 위한 키워드
class Animal {
constructor(name) {
this.speed = 0;
this.name = name;
}
run(speed) {
this.speed = speed;
alert(`${this.name} 은/는 속도 ${this.speed}로 달립니다.`);
}
stop() {
this.speed = 0;
alert(`${this.name} 이/가 멈췄습니다.`);
}
}
let animal = new Animal("동물");
class Rabbit extends Animal {
hide() {
alert(`${this.name} 이/가 숨었습니다!`);
}
}
let rabbit = new Rabbit("흰 토끼");
rabbit.run(5); // 흰 토끼 은/는 속도 5로 달립니다.
rabbit.hide(); // 흰 토끼 이/가 숨었습니다!
function f(phrase) {
return class {
sayHi() { alert(phrase) }
}
}
class User extends f("Hello") {}
new User().sayHi(); // Hello
: extends 뒤 표현식 사용 가능
메서드 오버라이딩 (super)
: 부모 메서드를 토대로 일부 기능만 변경하거나
부모 메서드의 기능을 확장할 때 커스텀 메서드를 만드는데
이때 부모 메서드를 호출하기 위한 개념
class Animal {
constructor(name) {
this.speed = 0;
this.name = name;
}
run(speed) {
this.speed = speed;
alert(`${this.name}가 속도 ${this.speed}로 달립니다.`);
}
stop() {
this.speed = 0;
alert(`${this.name}가 멈췄습니다.`);
}
}
class Rabbit extends Animal {
hide() {
alert(`${this.name}가 숨었습니다!`);
}
stop() {
super.stop(); // 부모 클래스의 stop을 호출해 멈추고,
this.hide(); // 숨습니다.
}
}
let rabbit = new Rabbit("흰 토끼");
rabbit.run(5); // 흰 토끼가 속도 5로 달립니다.
rabbit.stop(); // 흰 토끼가 멈췄습니다. 흰 토끼가 숨었습니다!
: super 키워드를 사용하여 메소드 오버라이딩 수행
: super.method(...)는 부모 클래스에 정의된 메서드를 호출
: super(...)은 부모 생성자를 호출하는데, 자식 생성자 내부에서만 사용 가능
: 화살표 함수에는 없음
생성자 오버라이딩
: 부모 클래스로 부터 상속 받은 메소드를 자식 클래스에서 확장 및 재정의
class Animal {
constructor(name) {
this.speed = 0;
this.name = name;
}
// ...
}
class Rabbit extends Animal {
constructor(name, earLength) {
this.speed = 0;
this.name = name;
this.earLength = earLength;
}
// ...
}
// 동작하지 않습니다!
let rabbit = new Rabbit("흰 토끼", 10); // ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
class Animal {
constructor(name) {
this.speed = 0;
this.name = name;
}
// ...
}
class Rabbit extends Animal {
constructor(name, earLength) {
super(name);
this.earLength = earLength;
}
// ...
}
// 이제 에러 없이 동작합니다.
let rabbit = new Rabbit("흰 토끼", 10);
alert(rabbit.name); // 흰 토끼
alert(rabbit.earLength); // 10
: super(...)은 this를 사용하기 전에 반드시 호출해야 함
클래스 필드 오버라이딩
: 자바스크립트에서 클래스 필드를 대상으로 오버라이딩을 하는 것
class Animal {
name = 'animal'
constructor() {
alert(this.name); // (*)
}
}
class Rabbit extends Animal {
name = 'rabbit';
}
new Animal(); // animal
new Rabbit(); // animal
: name 필드를 오버라이딩 했으나 동작하지 않음
: 부모 생성자는 자식 클래스에서 오버라이딩한 값이 아닌 부모 클래스 안의 필드 값을 사용하기 때문
class Animal {
showName() { // this.name = 'animal' 대신 메서드 사용
alert('animal');
}
constructor() {
this.showName(); // alert(this.name); 대신 메서드 호출
}
}
class Rabbit extends Animal {
showName() {
alert('rabbit');
}
}
new Animal(); // animal
new Rabbit(); // rabbit
: 필드 초기화 순서로 인해 Rabbit의 필드는 super() 실행 후에 초기화 되면서 원하는 결과가 나옴
super 키워드와 [[HomeObject]]
let animal = {
name: "동물",
eat() {
alert(`${this.name} 이/가 먹이를 먹습니다.`);
}
};
let rabbit = {
__proto__: animal,
eat() {
// call을 사용해 컨텍스트를 옮겨가며 부모(animal) 메서드를 호출합니다.
this.__proto__.eat.call(this); // (*)
}
};
let longEar = {
__proto__: rabbit,
eat() {
// longEar를 가지고 무언가를 하면서 부모(rabbit) 메서드를 호출합니다.
this.__proto__.eat.call(this); // (**)
}
};
longEar.eat(); // RangeError: Maximum call stack size exceeded
- [[HomeObject]]
: 위의 문제를 해결할 수 있는 함수 전용 특수 내부 프로퍼티
: super는 [[HomeObject]]를 이용해 부모 프로토타입과 메서드를 찾음
: [[HomeObject]]는 한번 바인딩된 함수가 변경되지 않기 때문에 자유롭지 않음
let animal = {
name: "동물",
eat() { // animal.eat.[[HomeObject]] == animal
alert(`${this.name} 이/가 먹이를 먹습니다.`);
}
};
let rabbit = {
__proto__: animal,
name: "토끼",
eat() { // rabbit.eat.[[HomeObject]] == rabbit
super.eat();
}
};
let longEar = {
__proto__: rabbit,
name: "귀가 긴 토끼",
eat() { // longEar.eat.[[HomeObject]] == longEar
super.eat();
}
};
// 이제 제대로 동작합니다
longEar.eat(); // 귀가 긴 토끼 이/가 먹이를 먹습니다.
Refference
'Programming > JavaScript' 카테고리의 다른 글
모던 자바스크립트 (클래스) 7-3. 내장 클래스 확장하기 / instanceof 로 클래스 확인 하기 / 믹스인 (0) | 2022.04.23 |
---|---|
모던 자바스크립트 (클래스) 7-2. 정적 메서드와 정적 프로퍼티 / private, protected 프로퍼티와 메서드 (0) | 2022.04.23 |
모던 자바스크립트 (프로토타입과 프로토타입 상속) 6-2. 내장 객체의 프로토타입 / 프로토타입 메서드와 __proto__가 없는 객체 (0) | 2022.04.23 |
모던 자바스크립트 (프로토타입과 프로토타입 상속) 6-1. 프로토타입 상속 / 함수의 prototype 프로퍼티 (0) | 2022.04.23 |
모던 자바스크립트 (객체 프로퍼티 설정) 5-2. 프로퍼티 getter와 setter (0) | 2022.04.23 |