자바스크립트 완벽 가이드 9장 정리 ( Class )
포스트
취소

자바스크립트 완벽 가이드 9장 정리 ( Class )

해당 포스트는 자바스크립트 완벽 가이드라는 교재로 스터디를 하면서 9장을 정리한 포스트입니다.
주관적으로 해석한 내용이 들어가 있어서 잘못된 내용이 포함될 수 있습니다.
또한 교재의 모든 내용을 정리하지 않고 주관적인 판단에 의해 필요한 내용만 작성했습니다.

🔨 객체 생성 방법

0️⃣ 팩토리 함수

팩토리 함수란 객체를 반환하는 함수를 의미합니다.
JavaScript에서는 쉽게 객체를 만들 수 있기 때문에 그렇게 의미있는 함수는 아닌 것 같지만, 아래와 같이 특별한 객체를 만드는 함수라면 의미 있는 함수인 것 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function range(from, to) {
  // 프로토 타입을 직접 정한 객체 생성
  let r = Object.create(range.methods);

  r.from = from;
  r.to = to;

  return r;
}

range.methods = {
  includes(v) {
    return this.from <= v && this.to > v;
  },

  *[Symbol.iterator]() {
    for (let index = this.from; index < this.to; index++) yield index;
  },

  toString() {
    return `(${this.from}...${this.to})`;
  },
};

const r = range(1, 3);

console.log(r.toString());
console.log(...r);

1️⃣ 생성자 함수

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function Range(from, to) {
  this.from = from;
  this.to = to;
}

Range.prototype = {
  includes(v) {
    return this.from <= v && this.to > v;
  },

  *[Symbol.iterator]() {
    for (let index = this.from; index < this.to; index++) yield index;
  },

  toString() {
    return `(${this.from}...${this.to})`;
  },
};

const r = new Range(1, 3);

console.log(r.toString());
console.log(...r);

2️⃣ 클래스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Range {
  constructor(from, to) {
    this.from = from;
    this.to = to;
  }

  includes(v) {
    return this.from <= v && this.to > v;
  }

  *[Symbol.iterator]() {
    for (let index = this.from; index < this.to; index++) yield index;
  }

  toString() {
    return `(${this.from}...${this.to})`;
  }
}

const r = new Range(1, 3);

console.log(r.toString());
console.log(...r);

🧾 객체 지향적 클래스 구현 예시

몇 가지 주요 키워드를 TypeScript에서만 지원해주기 때문에 TypeScript를 이용한 예시입니다.

  • 주요 키워드
    1. 가상 클래스/메서드 ( abstruct ) : 클래스는 인스턴스 생성 불가능 / 메서드는 자식에서 반드시 구현
    2. 정적 변수/메서드 ( static ) : 클래스 자체가 갖는 값 ( 즉, 속성을 갖는 값 )
    3. 상속 : 상위 요소에 선언된 변수/메서드를 그대로 사용
    4. 접근 제한자 ( private, protected, public ) : 접근의 범위 결정
    5. super : 상위 클래스
    6. gettersetter : 간접적으로 접근
    7. overriding : 하위 요소에서 덮어씌우는 방식
    8. _를 접두사로 붙인 변수는 직접적으로 호출하지 않는 게 관습

0️⃣ Animal 클래스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// (1) 가상 클래스
abstract class Animal {
  // (4) 접근 제한자: protected
  protected age: number;

  constructor(age: number) {
    this.age = age;
  }

  // (1) 가상 메서드
  public abstract move(): void;

  // (2) 정적 메서드: 동물이라면 누구나 갖는 동작이라 정적 메서드로 만듦
  public static breathe() {
    console.log("호흡");
  }
}

1️⃣ Dog 클래스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// ( 상속 )
abstract class Dog extends Animal {
  // (4) 접근 제한자: private ( with "_" )
  private _color: string;

  constructor(age: number, color: string) {
    // (5) super: 부모 생성자(constructor) 호출
    super(age);

    this._color = color;
  }

  // 강아지라면 갖는 동작
  public bark() {
    console.log("멍멍");
  }
  public eat() {
    console.log("먹음");
  }

  // (6) getter/setter
  protected get color() {
    return this._color;
  }
  protected set color(color: string) {
    if (!(color === "red" || color === "green" || color === "blue")) {
      throw new Error("red | green | blue만 사용할 수 있습니다.");
    }

    this._color = color;
  }
}

2️⃣ Maltese 클래스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Maltese extends Dog {
  private name: string;

  constructor(age: number, color: string, name: string) {
    // (5) super: 부모 생성자(constructor) 호출
    super(age, color);

    this.name = name;
  }

  // (1) 가상 메서드는 자식이 반드시 구현해야 함
  public move() {
    console.log(`${this.name}의 움직임`);
  }

  // (7): overriding 말티즈 만의 동작
  public eat() {
    console.log("이름이 " + this.name + "인 말티즈가");

    // (5) super: 상위 메서드 호출
    super.eat();
  }

  public intro() {
    console.log(`나이: ${this.age}
색상: ${this.color}
이름: ${this.name}`);
  }
}

3️⃣ 사용 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// (2) "호흡"
Maltese.breathe();

const m = new Maltese(2, "red", "강아지");

// 'name' 속성은 private이며 'Maltese' 클래스 내에서만 액세스할 수 있습니다.
// console.log(m.name);

// "이름이 강아지인 말티즈가"
// "먹음"
m.eat();

// "멍멍"
m.bark();

// "강아지의 움직임"
m.move();

// 나이: 2
// 색상: red
// 이름: 강아지
m.intro();

/**
 * ** 결과 **
 *
 * 호흡
 * 이름이 강아지인 말티즈가
 * 먹음
 * 멍멍
 * 강아지의 움직임
 * 나이: 2
 * 색상: red
 * 이름: 강아지
 */

🃏 알아두면 쓸모있는 잡다한 클래스 지식

0️⃣ instanceof와 isPrototypeOf()

교재에서는 instanceof가 확실하게 인스턴스인지 파악하지 못하는 예외의 경우를 보여주고 isPrototypeOf()를 사용하는 방법에 대해서 알려줍니다.
근데 실제로 이런 경우가 발생하려면 prototype을 직접적으로 수정하거나 팩토리 함수를 사용해서 객체를 만들어내야 합니다.
근데 두 가지 경우를 사용해본 경험이 없기도 하고, 사용하는 코드를 본적도 없기 때문에 “이렇게까지 해야하나?”라는 생각이 들어서 간단하게 instanceof를 유용하게 사용해본 경우에 대해서만 정리해보겠습니다.

TypeScript를 사용하다보면 변수의 타입을 좁혀줘야 하는 경우가 종종 발생합니다.
React에서도 e.target 혹은 ref.current같은 타입을 좁혀줘야 하는 경우도 많습니다.
그런 경우에 아래처럼 사용해서 타입 체커에게 타입을 확정시켜주는데 유용하게 사용합니다.

1
2
3
4
5
const $button = document.querySelector(".toggle-btn");

if($button instanceof HTMLButton) {
  // 블록 내부에서는 "$button"이 버튼 타입임을 확정
}

1️⃣ 몽키패치

donggov - 몽키패치란를 참고했습니다.

몽키패치란 원래 소스코드를 변경하지 않고 실행 시 코드 기본 동작을 추가, 변경 또는 억제하는 기술을 의미합니다.

많이 오래된 브라우저에서는 String.prototype.startsWith()를 지원하지 않을 수 있습니다.
하지만 저희는 많이 오래된 브라우저는 고려하지 않고 최신 문법을 이용해서 개발을 합니다.
그렇다면 startsWith()를 지원하지 않는 브라우저가 제가 만든 웹사이트를 들어오면 에러가 발생할 것입니다.

그런 경우에 런타임에 코드의 동작을 추가하는 방법을 사용합니다.
이런 기술을 몽키패치라고 하고 babel에서는 polyfill을 통해 구형 브라우저의 접근을 도와줍니다.

1
2
3
4
5
if (!String.prototype.startsWith) {
  String.prototype.startsWith = function (s) {
    return this.indexOf(s) === 0;
  };
}

2️⃣ 클래스와 생성자 함수의 차이

  1. 접근 제한자 ( public, protected, private )
  2. 가상 키워드 사용 가능 여부 ( abstract )
  3. 정적 키워드 사용 가능 여부 ( static )
  4. 나열 여부
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class CChampion {
  constructor(name, speed) {
    this.name = name;
    this.speed = speed;
  }

  attack() {
    console.log(this.name + ": 공격");
  }
}

const FChampion = function (name, speed) {
  this.name = name;
  this.speed = speed;
};
FChampion.prototype.attack = function () {
  console.log(this.name + ": 공격");
};

const c1 = new CChampion("Aatrox", 345);
const c2 = new FChampion("Puppy", 350);

for (const key in c1) {
  console.log(key); // name speed
}
for (const key in c2) {
  console.log(key); // name speed attack
}

3️⃣ 싱글톤 패턴

클래스의 인스턴스를 단 하나만 만드는 방법을 의미합니다.
( 자세한 내용은 싱글톤 참고 )

📮 레퍼런스

  1. « 자바스크립트 완벽 가이드 9장 » ( 데이비드 플래너건 지음, 한성용 옮김, 인사이트, 2022 )
  2. donggov - 몽키패치란
  3. 1-blue - polyfill
  4. 1-blue - 싱글톤
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.

연결 리스트

개인 프로젝트 - blequotes 마무리