320x100
320x100

클래스

: 객체지향 프로그래밍에서 특정 객체를 생성하기 위해 변수와 메소드를 정의하는 일종의 틀로

  객체를 정의하기 위한 상태인 멤버 변수와 메서드로 구성

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

 

클래스와 기본 문법

 

ko.javascript.info

 

클래스 상속

 

ko.javascript.info

 

300x250
728x90