게으른 나르의 코딩

[JavaScript] 클래스 본문

Languages/JavaScript

[JavaScript] 클래스

개발자 나르 2022. 6. 9. 16:37
반응형

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

  • 클래스를 new 연산자 없이 호출하면 에러가 발생한다. 하지만 생성자 함수를 new 연산자 없이 호출하면 일반 함수로서 호출된다.
  • 클래스는 상속을 지원하는 extends와 super 키워드를 제공한다. 하지만 생성자 함수는 지원하지 않는다.
  • 클래스는 호이스팅이 발생하지 않는 것처럼 동작한다. 하지만 함수 선언문으로 정의된 생성자 함수는 함수 호이스팅이, 함수 표현식으로 정의한 생성자 함수는 변수 호이스팅이 발생한다.
  • 클래스 내의 모든 코드에는 암묵적으로 strict mode가 지정되어 실행되며 strict mode를 해제할 수 없다. 하지만 생성자 함수는 암묵적으로 지정되지 않는다.
  • 클래스의 constructor, 프로토타입 메서드, 정적 메서드는 모두 프로퍼티 어트리뷰트 [[Enumerable]]의 값이 false다. 다시 말해, 열거되지 않는다.

클래스 정의

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

 

클래스의 특징

  • 무명의 리터럴로 생성할 수 있다. 즉, 런타임에 생성이 가능하다.
  • 변수나 자료구조(객체, 배열 등)에 저장할 수 있다.
  • 함수의 매개변수에게 전달할 수 있다.
  • 함수의 반환값으로 사용할 수 있다.

클래스 몸체에서 정의할 수 있는 메서드는 constructor(생성자), 프로토타입 메서드, 정적 메서드 이다.

//클래스 정의
class ClaMeth {
	//생성자
	constructor(num) {
    	//인스턴스 생성 및 초기화
    	this.num = num;
    }
    //프로토타입 메서드
    output() {
    	console.log(this.num);
    }
    //정적 메서드
    static out() {
    	console.log(2);
    }
}
//인스턴스 생성
const num = new ClaMeth(3);

//인스턴스의 프로퍼티 참조
console.log(num.num); // 3
//프로토타입 메서드 호출
num.output(); // 3
//정적 메서드 호출
ClaMeth.out(); // 2

클래스 호이스팅

클래스는 클래스 정의 이전에 참조할 수 없다.

 

인스턴스 생성

클래스는 인스턴스를 생성하는 것이 유일한 존재 이유이므로 반드시 new 연산자와 함께 호출해야 한다.

 

constructor

constructor는 인스턴스를 생성하고 초기화하기 위한 특수한 메서드다. constructor는 이름을 변경할 수 없다.

constructor는 클래스 내에 최대 한 개만 존재할 수 있고 생략 가능하다. 생략하면 빈 constructor가 암묵적으로 생성한다.

인스턴스를 초기화하려면 constructor를 생략해서는 안된다. 그리고 return문을 반드시 생략해야 한다.

 

프로토타입 메서드

클래스는 생성자 함수와 마찬가지로 프로토타입 기반의 객체 생성 메커니즘이기 때문에 클래스의 prototype 프로퍼티에 메서드를 추가하지 않아도 기본적으로 포로토타입 메서드가 된다.

 

정적 메서드

정적 메서드는 인스턴스를 생성하지 않아도 호출할 수 있는 메서드이다. 메서드 앞에 static 키워드를 붙이면 정적 메서드가 된다.

정적 메서드가 바인딩된 클래스는 인스턴스의 프로토타입 체인 상에 존재하지 않기 떄문에 인스턴스로 호출할 수 없다.

 

정적 메서드와 프로토타입 메서드의 차이

  • 정적 메서드와 프로토타입 메서드는 자신이 속해 있는 프로토타입 체인이 다르다.
  • 정적 메서드는 클래스로 호출하고 프로토타입 메서드는 인스턴스로 호출한다.
  • 정적 메서드는 인스턴스 프로퍼티를 참조할 수 없지만 프로토타입 메서드는 인스턴스 프로퍼티를 참조할 수 있다.

메서드 내부에서 인스턴스 프로퍼티를 참조할 필요가 있다면 this를 사용해야 하며, 프로토타입 메서드로 정의해야 한다.

하지만 참조할 필요가 없다면 this를 사용하지 않기 때문에 정적 메서드로 정의해야 한다.

정적 메서드는 어플리케이션 전역에서 사용할 유틸리티 함수를 전역 함수로 정의하지 않고 메서드로 구조화할 때 유용하다.

 

프로퍼티

인스턴스 프로퍼티

인스턴스 프로퍼티는 constructor 내부에서 정의해야 한다. 인스턴스 프로퍼티는 언제나 public이다.

 

접근자 프로퍼티

접근자 프로퍼티는 자체적으로 값을 갖지 않고 다른 데이터 프로퍼티의 값을 읽거나 저장할 때 사용하는 접근자 함수 getter/setter 함수로 구성되어 있다.

getter는 인스턴스 프로퍼티에 접근할 때 마다 프로퍼티 값을 조작하거나 별도의 행위가 필요할 때 사용한다. get 키워드 사용

setter는 인스턴스 프로퍼티에 값을 할당할 때 마다 프로퍼티 값을 조작하거나 별도의 행위가 필요할 때 사용한다.set 키워드 사용

getter는 무언가를 취득할 때 사용하므로 반드시 무언가를 반환해야하고 setter는 무언가를 프로퍼티에 할당해야할 때 사용하므로 반드시 매개변수가 있어야 한다. setter는 단 하나의 값만 할당받기 때문에 단 하나의 매개변수만 선언할 수 있다.

 

클래스 필드

클래스 몸체에서 클래스 필드를 정의하는 경우 this에 클래스 필드를 바인딩해서는 안된다. this는 클래스의 constructor와 메서드 내에서만 유효하다.

인스턴스를 생성할 때 외부의 초기값으로 클래스 필드를 초기화해야 할 필요가 있다면 constructor에서 클래스 필드를 초기화해야 한다.

클래스 필드에 함수를 할당하는 경우, 이 함수는 프로토타입 메서드가 아닌 인스턴스 메서드가 된다.

클래스 필드는 언제나 public이다.

 

pirvate 필드 정의

클래스 필드는 언제나 public이기 때문에 외부에 그대로 노출되어 변경이 쉽게 될 위험성이 있다. 그래서 pirvate이라는 접근 제한자를 구현했다.

pirvate 사용법은 필드의 선두에 # 키워드를 써준다. private 필드는 클래스 내부에서만 참조할  수 있기 때문에 클래스 외부에서 private 필드에 직접 접근할 수 있는 방법이 없어서 외부로부터 변경이 되지 않는다.

접근 가능성 public private
클래스 내부 O O
자식 클래스 내부 O X
클래스 인스턴스를 통한 접근 O X

private 필드는 반드시 클래스 몸체에 정의해야 한다. private 필드를 직접 constructor에 정의하면 에러가 발생한다.

 

상속

클래스 상속과 생성자 함수 상속

상속에 의한 클래스 확장과 프로토타입 기반 상속과는 다른 개념이다.

프로토타입 기반 상속은 프로토타입 체인을 통해 다른 객체의 자산을 상속받는 개념이라면 상속에 의한 클래스 확장은 기존 클래스를 상속받아 새로운 클래스를 확장하여 정의하는 것이다.

 

//클래스 상속
class Animal {
	constructor(age, weight) {
    	this.age = age;
        this.weight = weight;
    }
    eat() { return 'eat'; }
    move() { return 'move'; }
}

class Bird extends Animal {
	fly() { return 'fly'; }
}

//생성자 함수 상속
var Animal = (function() {
	function Animal(age, weight) {
    	this.age = age;
        this.weight = weight;
    }
    Animal.prototype.eat = function() {
    	return 'eat';
    };
    Animal.prototype.move = function() {
    	return 'move';
    };
    return Animal;
}());

var Bird = (function () {
	function Bird() {
    	Animal.apply(this, arguments);
    }
    
    Bird.prototype = Object.create(Animal.prototype);
    Bird.prototype.constructor = Bird;
    Bird.prototype.fly = function() {
    	return 'fly';
    };
    return Bird;
}());

코드 상에서도 가독성이 좋은건 class 상속이 더욱 좋고 복잡하지 않고 간단하다.

 

extends 키워드

상속을 통해 확장된 클래스를 서브클래스라 부르고, 서브클래스에게 상속된 클래스를 수퍼클래스라 부른다.

서브클래스 또 다른 명칭 : 파생 클래스, 자식 클래스

수퍼클래스 또 다른 명칭 : 베이스 클래스, 부모 클래스

 

extends 키워드는 클래스만 상속받는게 아니라 생성자 함수를 상속받아 클래스를 확장할 수 있다. 그러나 서브클래스는 무조건 클래스여야 된다.

 

super 키워드

super 키워드는 함수처럼 호출할 수도 있고 this와 같이 식별자처럼 참조할 수 있는 특수한 키워드다.

  • super를 호출하면 수퍼클래스의 constructor를 호출한다.
  • super를 참조하면 수퍼클래스의 메서드를 호출할 수 있다.
//수퍼 클래스
class Base {
	constructor(a, b) {
    	this.a = a;
        this.b = b;
    }
    output() {
    	return `${this.a} , ${this.b}`;
    }
}
const base = new Base(1,2);
console.log(base.output()); // 1 , 2
//서브 클래스
class Derived extends Base {
	constructor(a, b, c) {
    	super(a,b);
        this.c = c;
    }
    output() {
    	return `${super.output()} , ${this.c}`;
    }
    sum() {
    	return this.a + this.b + this.c;
    }
}

const derived = new Derived(2,3,4);
console.log(derived.output()); // 2 , 3 , 4
console.log(derived.sum()); // 9

super를 호출할 때 주의 사항

  • 서브클래스에서 constructor를 생략하지 않는 경우 서브클래스의 constructor에서는 반드시 super를 호출해야 한다.
  • 서브클래스의 constructor에서 super를 호출하기 전에는 this를 참조할 수 없다.
  • super는 반드시 서브클래스의 constructor에서만 호출한다. 서브클래스가 아닌 클래스의 constructor나 함수에서 super를 호출하면 에러가 발생한다.

super 참조

메서드 내에서 super를 참조하면 수퍼클래스의 메서드를 호출할 수 있다.

  • 서브클래스의 프로토타입 메서드 내에서 super.메서드명 은 수퍼클래스의 프로토타입 메서드 메서드명을 가리킨다.
  • 서브클래스의 정적 메서드 내에서 super.메서드명은 수퍼클래스의 정적 메서드 메서드명을 가리킨다.
반응형

'Languages > JavaScript' 카테고리의 다른 글

[JavaScript] 배열이란  (0) 2022.06.09
[JavaScript] ES6 함수의 추가 기능  (0) 2022.06.09
[JavaScript] 클로저  (0) 2022.06.08
[JavaScript] 실행 컨텍스트  (0) 2022.06.08
[JavaScript] this  (0) 2022.06.08
Comments