프로그래밍/JavaScript

ECMA6 - Class

가라멜 2019. 4. 29. 16:52
반응형

출처 : https://poiemaweb.com/es6-class

 

Class | PoiemaWeb

자바스크립트는 프로토타입 기반(prototype-based) 객체지향형 언어다. 비록 다른 객체지향 언어들과의 차이점에 대한 논쟁들이 있긴 하지만, 자바스크립트는 강력한 객체지향 프로그래밍 능력들을 지니고 있다. 프로토타입 기반 프로그래밍은 클래스가 필요없는(class-free) 객체지향 프로그래밍 스타일로 프로토타입 체인과 클로저 등으로 객체 지향 언어의 상속, 캡슐화(정보 은닉) 등의 개념을 구현할 수 있다. 하지만 클래스 기반 언어에 익숙한 프로그래

poiemaweb.com

 

 

이 포스팅의 위 출처의 게시글을 공부하며 필기 하듯이 써놓은 내용이므로,

정확한 내용은 위의 출처에서 보는 것이 좋다.

 

자바스크립트는 프로토타입 기반 객체지향 언어이다. 비록 다른 객체지향 언어들과의 차이점에 대한 논쟁이 있긴 하지만, 자바스크립트는 강력한 객체지향 프로그래밍 능력을 지니고 있다.

 

프로토타입 기반 프로그래밍은 클래스가 필요없는 객체지향 프로그래밍 스타일로 프로토타입 체인과 클로저 등으로 객체지향 언어의 상속, 캡슐화 등의 개념을 구현할 수 있다.

 

1. 클래스 정의

ES6 클래스는 class 키워드를사용하여 정의한다.

class Person {
  constructor(name) {
    this._name = name;
  }

  sayHi() {
    console.log(`Hi! ${this._name}`);
  }
}

const me = new Person('Lee');
me.sayHi(); // Hi! Lee

console.log(me instanceof Person); // true

 

일반적이지는 않지만, 표현식으로도 클래스를 정의할 수 있다. 함수와 마찬가지로 클래스는이름을 가질 수도 갖지 않을 수도 있다. 이때 클래스가 할당된 변수를 사용해 클래스를 생성하지 않고기명 클래스의 클래스 이름을 사용해 클래스를 생성하면 에러가 발생한다. 이는 함수와 마찬가지로 클래스 ㅍ현식에서 사용한 클래스 이름은 외부 코드에서 접근 불가능하기 때문이다.

 

2. 인스턴스의 생성

클래스의 인스턴스를 생성하려면 new 연산자와 함께 constructor를 호출한다.

class Foo {}
const foo = new Foo();

new 연산자를 사용하지 않고 생성자를 호출하면 타입 에러가 발생한다. 

 

3, constructor(생성자)

생성자는 인스턴스를 생성하고 클래스 프로퍼티를 초기화하기 위한 특수한 메소드이다. 생성자는 클래스 내에 한 개만 존재할 수 있으며 만약 클래스가 2개 이상의 생성자를 포함하면 문법 에러가 발생한다. 인스턴스를 생성할 때 new 연산자와 함께 호출한 것이 바로 생성자(constructor) 이며 생성자 파라미터에 전달한 값은 클래스 프로퍼티에 할당한다.

생성자는 생략할 수 있다. 생성자를 생략하면 클래스에 constructor() } 를 포함한 것과 동일하게 동작한다. 즉, 빈 객체를 생성한다. 따라서 클래스 프로퍼티를 선언하려면 인스턴스를 생성한 이후, 클래스 프로퍼티를 동적 할당해야 한다.

4. 크래스 프로퍼티

클래스 몸체에는 메소드만 선언할 수 있다. 클래스 몸체에 클래스 프로퍼티(인스턴스 필드, 멤버 변수)를 선언하면 문법에러가 발생한다.

class Foo {
  name = ''; // SyntaxError

  constructor() {}
}

 

클래스 프로퍼티의 선언과 초기화는반드시 생성자(constructor) 내부에서 실시한다.

class Foo {
  constructor(name = '') {
    this.name = name; // 클래스 프로퍼티의 선언과 초기화
  }
}
const foo = new Foo('Lee');
console.log(foo); // Foo { name: 'Lee' }

생성자 내부에서 선언한 클래스 프로퍼티는 클래스의 인스턴스를 가리키는 this에 바인딩한다. 이로써 클래스 프로퍼티는 클래스가 생성할 인스턴스의 프로퍼티가 되며, 클래스의 인스턴스를 통해 클래스 외부에서 언제나 참조할 수 있다.

즉 언제나 public 이다.

class Foo {
  constructor(name = '') {
    this.name = name; // public 클래스 프로퍼티
  }
}

const foo = new Foo('Lee');
console.log(foo.name); // 클래스 외부에서 참조할 수 있다.

 

5. 호이스팅

클래스는 let, const 와 가이 호이스팅되지 않는 것처럼 동작한다. 즉, class 선언문 이전에 클래스를 참조하면 참조 에러가 발생한다.

const foo = new Foo(); //referenceError
class Foo{}

자바스크립트는 ES6의 class 를포함하여 모든 선언을 호이스팅한다. 하지만 클래스는 스코프의 선두에서 선언문에 도달할 때까지 일시적 사각지대(Temporal Dead Zone; TDZ)에 빠진다. 따라서 클래스 선언문 이전에 클래스를 참조하면 참조에러가 발생한다.

 

6. getter, setter

6.1 getter

getter 는 클래스 프로퍼티에 접근할 때마다 클래스 프로퍼티의 값을 조작하는 행위가 필요할 때 사용한다.

getter는 메소드 이름 앞에 get 키워드를 사용해 정의한다. 이때 메소드 이름은 클래스 프로퍼티 이름처럼 사용된다. 다시 말해 getter 는 호출하는 것이 아니라 프로퍼티처럼 참조하는 형식으로 사용하며 참조 시에 메소드가 호출된다. getter 는 이름 그대로 무언가를 취득할 때 사용하므로 반드시 무언가를 반환해야 한다.

class Foo {
  constructor(arr = []) {
    this._arr = arr;
  }

  // getter: get 키워드 뒤에 오는 메소드 이름 firstElem은 프로퍼티 이름처럼 사용된다.
  get firstElem() {
    // getter는 반드시 무언가를 반환해야 한다.
    return this._arr.length ? this._arr[0] : null;
  }
}

const foo = new Foo([1, 2]);
// 프로퍼티 firstElem에 접근하면 getter가 호출된다.
console.log(foo.firstElem); // 1

 

6.2 setter

setter 는 클래스 프로퍼티에 값을 할당할 때마다 클래스 프로퍼티의 값을 조작하는 행위가 필요할 때 사용한다.

setter는 메소드 이름 앞에 set 키워드를 사용해 정의한다. 이때 메소드 이름은클래스 프로퍼티 이름처럼 사용된다.

다시말해 setter 는 호출하는 것이 아니라 프로퍼티처럼 값을 할당하는 형식으로 사용하며 할당시에 메소드가 호출 된다.

class Foo {
  constructor(arr = []) {
    this._arr = arr;
  }

  // getter: get 키워드 뒤에 오는 메소드 이름 firstElem은 프로퍼티 이름처럼 사용된다.
  get firstElem() {
    // getter는 반드시 무언가를 반환하여야 한다.
    return this._arr.length ? this._arr[0] : null;
  }

  // setter: set 키워드 뒤에 오는 메소드 이름 firstElem은 프로퍼티 이름처럼 사용된다.
  set firstElem(elem) {
    // ...this._arr은 this._arr를 개별 요소로 분리한다
    this._arr = [elem, ...this._arr];
  }
}

const foo = new Foo([1, 2]);

// 프로퍼티 lastElem에 값을 할당하면 setter가 호출된다.
foo.firstElem = 100;

console.log(foo.firstElem); // 100

 

 

7. 정적 메소드

클래스의 정적(static) 메소드를 정의할 때 static 키워드를 사용한다. 정적 메소드는 클래스의 인스턴스가 아닌 클래스 이름으로 호출한다. 따라서 클래스의 인스턴스를 생성하지 않아도 호출할 수 있다.

class Foo {
  constructor(prop) {
    this.prop = prop;
  }

  static staticMethod() {
    /*
    정적 메소드는 this를 사용할 수 없다.
    정적 메소드 내부에서 this는 클래스의 인스턴스가 아닌 클래스 자신을 가리킨다.
    */
    return 'staticMethod';
  }

  prototypeMethod() {
    return this.prop;
  }
}

// 정적 메소드는 클래스 이름으로 호출한다.
console.log(Foo.staticMethod());

const foo = new Foo(123);
// 정적 메소드는 인스턴스로 호출할 수 없다.
console.log(foo.staticMethod()); // Uncaught TypeError: foo.staticMethod is not a function

 

클래스의 정적 메소드는 인스턴스로 호출할 수 없다. 이것은 정적 메소드는 this를 사용할 수 없다는 것을 의미한다. 일반 메소드 내부에서 this는 클래스의 인스턴스를 가리키며, 메소드 내부에서 this를 사용한다는 것은 클래스의 인스ㅓㄴ스의 생성을 전제로 하는 것이다. 

정적 메소드는 클래스 이름으로 호출하기 때문에 클래스의 인스턴스를 생성하지 않아도 사용할 수 있다. 단, 정적 메소드는 this를 사용할 수 없다. 달리 말하면 메소드 내부에서 this를 사용할 필요가 없는 메소드는 정적 메소드로 만들 수 있다. 정적 메소드는 Math 객체의 메소드처럼 애플리케이션 전역에서 사용할 유틸리티 함수를 생성할 때 주로 사용한다.

 

8. 클래스 상속

클래스 상속은 코드 재사용 관점에서 매우 유용하다. 새롭게 정의할 클래스가 기존에 있는 클래스와 매우 유사하다면, 상속을 통해 그대로 사용하되 다른 점만 구현하면 된다. 코드 재사용은 개발 비용을 현저히 줄일 수 있는 잠재력이 있으므로 매우 중요하다.

 

8.1 extends 키워드

exteds 키워드는 부모 클래스를 상속받는 자식 클래스를 정의할 때 사용한다. 

// 부모 클래스
class Circle {
  constructor(radius) {
    this.radius = radius; // 반지름
  }

  // 원의 지름
  getDiameter() {
    return 2 * this.radius;
  }

  // 원의 둘레
  getPerimeter() {
    return 2 * Math.PI * this.radius;
  }

  // 원의 넓이
  getArea() {
    return Math.PI * Math.pow(this.radius, 2);
  }
}

// 자식 클래스
class Cylinder extends Circle {
  constructor(radius, height) {
    super(radius);
    this.height = height;
  }

  // 원통의 넓이: 부모 클래스의 getArea 메소드를 오버라이딩하였다.
  getArea() {
    // (원통의 높이 * 원의 둘레) + (2 * 원의 넓이)
    return (this.height * super.getPerimeter()) + (2 * super.getArea());
  }

  // 원통의 부피
  getVolume() {
    return super.getArea() * this.height;
  }
}

// 반지름이 2, 높이가 10인 원통
const cylinder = new Cylinder(2, 10);

// 원의 지름
console.log(cylinder.getDiameter());  // 4
// 원의 둘레
console.log(cylinder.getPerimeter()); // 12.566370614359172
// 원통의 넓이
console.log(cylinder.getArea());      // 150.79644737231007
// 원통의 부피
console.log(cylinder.getVolume());    // 125.66370614359172

// cylinder는 Cylinder 클래스의 인스턴스이다.
console.log(cylinder instanceof Cylinder); // true
// cylinder는 Circle 클래스의 인스턴스이다.
console.log(cylinder instanceof Circle);   // true

 

8.2 super 키워드

super 키워드는 부모 클래스를 참조 할 때 또는 부모 크랠스의 construcotr 를 호출할 때 사용한다.

 

 

반응형

'프로그래밍 > JavaScript' 카테고리의 다른 글

ECMA6 - Promise  (0) 2019.05.01
ECMA6 - Module  (0) 2019.04.30
ECMA6 - 디스트럭처링  (0) 2019.04.28
ECMA6 - Enhanced Object property  (0) 2019.04.27
ECMA6 - Extended Parameter Handling  (0) 2019.04.26