class

mangojang·2023년 1월 10일
0

✍️ 면접 때 class 함수에 관한 질문을 받았었다. 객체지향형 프로그래밍 하면 class기반이 대표적이다. (Java, C++, C#, Python, PHP, Ruby, Object-C) 반면에 javascript는prototype 기반이다.(생성자 함수) es6 부터 javascript 도 class 문법을 지원 하기 시작 하였다. extends와 super 를 통한 상속, Object.assign을 이용해 믹스인 하는 방법 등 유용하게 쓸 것 같다.

사용하는 상황

  • 동일한 종류의 객체를 여러 개 생성 해야 할 때
  • 상속과 캡슐화가 필요한 객체 지향형 코드를 구현 해야 할 때 👉 캡슐화( encapsulation)
    1. 객체의 속성(data fields)과 행위(메서드, methods)를 하나로 묶고,
    2. 실제 구현 내용 일부를 외부에 감추어 은닉 한다

기본 문법

class User {
	constructor(name){
		this.name = name; 
	}

	introduceMyself(){
		console.log(`my name is ${this.name}`);
	}
}

let user = new User('mangojang');
user.introduceMyself() // my name is mangojang;
  • constructor, introduceMyself (method) 모두 User 함수의 prototype에 속함.
  • class 함수명도 생성자 함수 처럼 앞에 대문자로 표기
  • 호출은 생성자 함수 처럼 new 연산자를 앞에 붙임.

getter, setter

getter

  • constructor 안의 값을 가져오기 위한 method
  • 메서드명 앞에 get 을 붙여 정의.

setter

  • constructor 안의 값을 조작하기 위한 method
  • 메서드명 앞에 set을 붙여 정의.
class User {
	constructor(name){
		this.name = name; 
	}

	get name(){
		return this._name;
	}

	set name(value){
		if (!value.trim().length){
			alert('이름을 다시 확인 해 주세요.')
			return;
		}
		this._name = value;
	}
}

let user = new User('mangojang');
console.log(user.name); // mangojang
user = new User(" "); // 이름으 다시 확인 해 주세요.
 

클래스 필드

  • 속성 명 = 값
class Mangojang {
	name = 'mangojang'

	introduceMyself(){
		console.log(`my name is ${this.name}`);
	}
}

new Mangojang().introduceMyself(); // my name is mangojang
  • Mangojang.prototype 이 아닌, 개별 객체

활용 방법

  • 클래스 필드 이용해서 바인딩 된 메서드 만들기
    ✅이벤트 리스너로 설정 해야 될 때 유용.
    class Button {
      constructor(value) {
        this.value = value;
      }
    
      click() {
        alert(this.value);
      }
    }
    
    let button = new Button("click!");
    
    setTimeout(button.click, 1000); // undefined
    • 위의 코드의 경우 setTimeout을 실행 했을 시, Button의 value 인 “안녕하세요” 가 아닌 undefined를 반환 함.
      ▶️ setTimeout은 this를 전역으로 바인딩 하기 때문.
    • 해결 방법
      1. function 으로 한번 더 감싸서 스코프를 준다.

        setTimeout(()=>button.click, 1000); 
      2. .bind 로 Button 바인딩

        let click = button.click.bind(button);
        setTimeout(click 1000); 
      3. 클래스 필드 이용하기

        class Button {
          constructor(value) {
            this.value = value;
          }
          click = () => {
            alert(this.value);
          }
        }
        
        let button = new Button("click!");
        
        setTimeout(button.click, 1000); // click!

상속

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 {
// Animal run 메서드를 오버라이딩(= 덮어쓰기) 함 .
	run(speed) {
    this.speed = speed + 10;
    alert(`${this.name}가 속도 ${this.speed}로 달립니다.`);
  }
// hide 메서드 확장
  hide() {
    alert(`${this.name} 이/가 숨었습니다!`);
  }
}

let rabbit = new Rabbit("흰 토끼");

// Animal 속성을 상속 받은 Rabbit은 speen, name 속성과, run, stop 메서드를 포함하고 있음.
rabbit.run(5); // 흰 토끼 은/는 속도 15로 달립니다.
rabbit.hide(); // 흰 토끼 이/가 숨었습니다!

super

  • 부모의 메서드를 그대로 쓰면서 확장 시키고자 한다면 super 사용.

🚫 화살표 함수는 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(); // 부모 클래스의 메서드 상속
    this.hide(); // 확장
  }
}

let rabbit = new Rabbit("흰 토끼");

rabbit.run(5); // 흰 토끼가 속도 5로 달립니다.
rabbit.stop(); // 흰 토끼가 멈췄습니다. 흰 토끼가 숨었습니다!
  • constructor 상속, 인자 확장 하고자 할 때도 super 사용
class Animal {

  constructor(name) {
    this.speed = 0;
    this.name = name;
  }

  // ...
}

class Rabbit extends Animal {

  constructor(name, earLength) { // earLength 인자 추가
    super(name);
    this.earLength = earLength;
  }

  // ...
}

let rabbit = new Rabbit("흰 토끼", 10);
alert(rabbit.name); // 흰 토끼
alert(rabbit.earLength); // 10

static

정적 메서드

  • prototype이 아닌 클래스 함수 자체에 메서드 설정
  • 인스턴스의 this를 호출 하지 않음. 클래스 전체의 this를 호출 함.

▶️ 내부 this의 사용이 필요 없는 경우, utility 함수를 정의 할 때 주로 사용.

class Triple {
  static triple(n) {
    n = n || 1; //비트연산이 아니어야 합니다.
    return n * 3;
  }
}

class BiggerTriple extends Triple {
  static triple(n) {
    return super.triple(n) * super.triple(n);
  }
}

console.log(Triple.triple());        // 3
console.log(Triple.triple(6));       // 18
console.log(BiggerTriple.triple(3)); // 81
var tp = new Triple();
console.log(BiggerTriple.triple(3)); // 81 (부모의 인스턴스에 영향을 받지 않습니다.)
console.log(tp.triple()); // 'tp.triple은 함수가 아닙니다.'.
console.log(tp.constructor.triple(4)); // 12

정적 프로퍼티

class Article {
  static publisher = "Ilya Kantor";
}

alert( Article.publisher ); // Ilya Kantor

public, private, protected

public

  • 외부 인터페이스 , 클래스 밖에서도 접근 가능한 프로퍼티 와 메서드 ,
  • 기본형

private

  • 내부 인터페이스 , 동일한 클래스 내에서는 메서드에 접근 할 수 있지만, 클래스 밖에선 접근 할 수 없음 .
  • #’ 을 앞에 붙여서 사용.
  • private 프로퍼티는 일반 public메서드로 접근 불가능
  • getter, setter 로만 접근 가능
  • 자손 클래스는 접근 불가능
class CoffeeMachine {

  #waterAmount = 0;

  get waterAmount() {
    return this.#waterAmount;
  }

  set waterAmount(value) {
    if (value < 0) throw new Error("물의 양은 음수가 될 수 없습니다.");
    this.#waterAmount = value;
  }
}

let machine = new CoffeeMachine();

machine.waterAmount = 100; // setter 실행
alert(machine.waterAmount); // 100
alert(machine.#waterAmount); // Error
  • 브라우저 지원 현황 추가 + 폴리필

protected

  • private 과 비슷 하지만 자손 클래스에서도 접근 가능
    ▶️자바스크립트는 지원 하지 않지만 비슷하게 모방에서 사용 가능함. ( getter, setter 이용)
  • 밑줄 ‘_’ 을 앞에 붙여 사용. ( 개발자 관습, 외부 접근이 불가능한 프로퍼티나 메서드를 나타낼때 사용)
class CoffeeMachine {
  _waterAmount = 0;

  set waterAmount(value) {
    if (value < 0) throw new Error("물의 양은 음수가 될 수 없습니다.");
    this._waterAmount = value;
  }

  get waterAmount() {
    return this._waterAmount;
  }

  constructor(power) {
    this._power = power;
  }

}

// 커피 머신 생성
let coffeeMachine = new CoffeeMachine(100);

// 물 추가
coffeeMachine.waterAmount = -10; // Error: 물의 양은 음수가 될 수 없습니다.
  • 읽기 전용은 setter 는 만들지 않고 getter 만 만듦.
class CoffeeMachine {
  // ...

  constructor(power) {
    this._power = power;
  }

  get power() {
    return this._power;
  }

}

// 커피 머신 생성
let coffeeMachine = new CoffeeMachine(100);

alert(`전력량이 ${coffeeMachine.power}인 커피머신을 만듭니다.`); // 전력량이 100인 커피머신을 만듭니다.

coffeeMachine.power = 25; // Error (setter 없음)

타입 확인

instanceof

  • obj instanceof Class
  • class 에 속하면 true 반환

믹스인

  • class 는 다중 상속이 안되는데, 메서드의 확장성을 더 넓히기 위해 사용하는 방법
  • Object.assign() 을 사용해서 메서드 객체(sayHiMixin)를 복사 해서 User.prototype 에 넣은후 User 반환
// 믹스인
let sayHiMixin = {
  sayHi() {
    alert(`Hello ${this.name}`);
  },
  sayBye() {
    alert(`Bye ${this.name}`);
  }
};

// 사용법:
class User {
  constructor(name) {
    this.name = name;
  }
}

// 메서드 복사
Object.assign(User.prototype, sayHiMixin);

// 이제 User가 인사를 할 수 있습니다.
new User("Dude").sayHi(); // Hello Dude!

참고 문헌

profile
한 걸음 한 걸음 계속 걷는 자가 일류다

0개의 댓글