자바스크립트 클래스 정리

허대훈·2022년 2월 17일
1

🎈 자바스크립트 클래스 정리

  • 생성자 함수
  • 프로토타입
  • 클래스 문법
  • 클래스 정의 방식 비교(생성자 함수 vs 클래스)
  • 클래스 호이스팅

[정리]

1. 클래스 정의

1-1. 생성자 함수

함수를 통해서 new 연산자와 함께 호출해서 빈 객체를 생성하여 반환한다. 이후 프로퍼티 또는 메서드를 추가하여 객체를 완성시킬 수 있다. 새로운 객체(인스턴스)를 만들고 사용자가 요구하는 함수들을 구현할 수 있게 해주는데, 생성자 함수에 의해 생성된 객체를 인스턴스(Instance)라 한다.

const player = new Object();

player.name = 'Choi';
player.sayWinner = function () {
	console.log('Ladies and gentlemen, Champion is ' + this.name);
};

player.sayWinner();

결과는 다음과 같다.
Ladies and gentlemen, Champion is Choi

1-1-1. 객체 리터럴에 의한 객체 생성 방식

필히 객체 생성자 함수를 사용해 빈 객체를 생성하는 것은 아니다. 객체를 생성하는 방법에는 객체 리터럴이 있고, 이 방법이 직관적이고 간편하다. 하지만, 단 하나의 객체만 생성하기 때문에 동일한 프로퍼티를 갖는 객체들을 생성해야 하는 경우에는 같은 프로퍼티를 계속해서 작성해야 하기 때문에 비효율적이다.

const rabbit = {
	type: '토끼',
	name: '빙키',
	sound: '깡총',
	say() {
		console.log(this.sound);
	}
};

console.log(rabbit.say());

const frog = {
	type: '개구리',
	name: '개리',
	sound: '개굴',
	say() {
		console.log(this.sound);
	}
};

console.log(frog.say());

결과는 다음과 같다.
깡총
개굴

위 코드에서 rabbit 객체와 frog 객체는 프로퍼티 구조가 동일하다. 객체 상태 데이터인 type, name, sound 프로퍼티 값은 다르지만, say 메서드는 같다. 이렇게 프로퍼티 구조가 같음에도 불구하고 매번 기술할 때마다 동일한 프로퍼티와 메서드를 나타내야 한다는 점이 물론, 한 두개의 객체를 표현할 때에는 번거롭지 않지만, 수 많은 객체를 생성해야 한다는 문제를 해결하기는 어렵다.


1-1-2. 생성자 함수에 의한 객체 생성 방식

흔히 문서 파일로 생성하기 위해 복사하여 사용하는 템플릿처럼 생성자 함수를 이용하여 프로퍼티 구조가 동일한 객체들을 여러 개 생성할 수 있는 방식이다.

function Animal(type, name, sound) {
  this.type = type;
  this.name = name;
  this.sound = sound;
  this.say = function() {
    console.log(this.sound);
  };
}

const rabbit = new Animal('토끼', '빙키', '깡총');
const frog = new Animal('개구리', '개리', '개굴');

rabbit.say();
frog.say();

결과는 다음과 같다.
깡총
개굴

생성자 함수는 C++, JAVA와 같은 클래스 기반 객체지향 언어와는 다르게 형식이 정해져 있지 않아서 일반 함수와 동일한 방법으로 정의할 수 있지만, new 연산자와 함께 호출하고 함수의 첫 글자는 대문자로 기술하는 것이 일반적인 관례이다.


1-2. 프로토타입

프로토타입은 사전 그대로 원형을 의미한다. 같은 생성자로부터 만들어진 객체들은 모두 원래의 모습인 이 원형 객체를 공유한다. 위 생성자 함수의 예제코드에서 살펴보았듯이 객체 rabbit과 frog가 동일한 say() 메서드를 수행하고 있는데, 이처럼 같은 객체 생성자 함수를 사용할 때 함수 또는 값을 재사용 할 수 있습니다. 이것이 바로 프로토타입이다.

프로토타입은 부모(상위) 객체의 역할을 하는데 다른 객체에 프로퍼티를 공유할 수 있다. 프로토타입을 상속받은 자식(하위) 객체는 부모 객체의 프로퍼티를 자신의 것으로 사용할 수 있다.

생성자 함수는 기본적으로 prototype 프로퍼티를 가지고 있는데, 이는 함께 만들어지는 프로토타입을 말하고, 프로토타입도 constructor라는 프로퍼티를 가져 생성자 함수를 말한다. 고로 둘의 관계는 뗄 수 없는 짝꿍의 개념이다.

  • 생성자 함수의 prototype 프로퍼티 ⇒ 프로토타입
  • 프로토타입의 constructor 프로퍼티 ⇒ 생성자 함수

자바스크립트는 프로토타입을 기반으로 상속을 구현해서 불필요한 중복을 제거하는데, 코드 재사용이 개발에 엄청난 효율성을 가져다 주기 때문에 이 점이 매우 중요하다.


1-3. 클래스 문법

1-3-1. Syntax Sugar(문법적 설탕)

자바스크립트는 프로토타입 기반의 객체지향 언어이다. 따라서 클래스가 필요 없어도 생성자 함수와 프로토타입을 통해 객체지향 언어에서 사용하는 상속을 사용할 수 있다. 하지만, C#과 자바와 같은 다른 클래스 기반의 언어에 익숙한 프로그래머들은 위 기반의 방식에 혼란을 느낄 수 있었다.

ES6부터 도입된 클래스는 다른 클래스 기반의 언어에 익숙한 프로그래머들에게 자바스크립트 학습에 대한 도움을 줄 수 있도록 매우 비슷한 객체 생성 프로세스를 제시한다. 그렇다고 해서 자바스크립트가 기존의 프로토타입 기반의 객체지향 모델을 져버리지는 않고, 기존 프로토타입 기반의 패턴을 클래스 기반 패턴처럼 사용할 수 있도록 해주는 편의문법(문법적 설탕)이라고 볼 수 있다.

주의해야할 점은 클래스와 생성자 함수간 차이점은 분명 존재한다.

  • new 연산자 없이 호출하면 클래스에서는 에러가 발생하고, 생성자 함수에서는 일반 함수로서 호출된다.
  • 상속을 지원하는 extend와 super 키워드를 클래스에서는 지원, 생성자 함수에서는 미지원한다.
  • 클래스 내 모든 코드에는 strict mode가 지정되어 있으며, 이를 해제할 수 없다. 반면, 생성자 함수는 strict mode가 지정되어 있지 않다.

간단히 요약해보면, 클래스가 생성자 함수보다 더욱 엄격하며 생성자 함수에서 제공하지 않는 기능을 제공한다는 점이다.


1-3-2. 클래스 사용

클래스는 class 키워드를 사용하여 정의할 수 있는데, 생성자 함수와 동등하게 첫 번째 글자를 대문자로 사용하는 파스칼 케이스를 사용하는 것이 일반적이다. 물론 파스칼 케이스를 사용하지 않아도 에러가 발생하지는 않는다.

class Player {}

1-3-3. 표현식

클래스를 표현식으로 정의할 수 있다는 얘기는 클래스가 값으로 사용할 수 있다는 것이다. 이는 클래스가 일급 객체의 특징을 갖는다는 것을 의미한다. 아래는 일급 객체의 특징을 나타낸다.

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

더욱 자세하게 살펴보면, 클래스는 함수이다. 따라서 클래스를 값처럼 사용할 수 있다.


class Player {}

console.log(typeof Player);

결과는 다음과 같다.
function

클래스는 함수와 마찬가지로 이름을 가질 수 있는 기명 클래스 표현식과 갖지 않을 수 있는 익명 클래스 표현식으로 나타낼 수 있다.

const Player = class {};  // 익명 클래스 표현식

const Player = class myClass {}; // 기명 클래스 표현식

1-4. 정의 방식 비교

생성자 함수는 function 뒤에 함수명을 적는 반면, 클래스는 class 키워드 뒤에 클래스명을 적어 정의한다.

1-4-1. 생성자 함수

var Player = (function () {

function Player(name) {
	this.name = name;
	}

// 프로토타입 메서드
Player.prototype.sayWinner = function () {
	console.log('Ladies and gentlemen, Champion is ' + this.name);
	};

// 정적 메서드
Player.sayWinner = function () {
	console.log('Champion!');
	};

return Player;
}());

Player.sayWinner();

결과는 다음과 같다.
Champion!

1-4-2. 클래스

class Player {

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

// 프로토타입 메서드
sayWinner() {
	console.log('Ladies and gentlemen, Champion is ${this.name}');
	}

// 정적 메서드
static sayWinner() {
	console.log('Champion!');
	}
}

Player.sayWinner();

결과는 다음과 같다.
Champion!

2. 클래스 호이스팅

앞선 5단원 var의 호이스팅 부분에서도 언급을 하였지만, 클래스의 선언문으로 정의한 클래스는 소스코드 평가 과정에서 실행하기 이전에 먼저 평가되어 함수 객체를 생성하는데 이것이 바로 호이스팅이다.

쉽게 말해서, 모든 선언문을 먼저 찾아내서 실행하고 난 뒤에 이후 모든 선언문을 제외한 소스코드부터 순차적으로 실행하는데, 이 때 클래스 선언문이 최상단으로 끌어올려져 동작하는 것을 의미한다.

이렇게 생성된 함수 객체는 생성자 함수로서 호출할 수 있는 함수인데 프로토타입과 더불어 같이 생성된다. 생성자 함수와 프로토타입은 어느 하나 단독으로 존재할 수 없고 항상 같이 존재하기 때문이다.

주의해야할 점은 클래스는 정의하기 이전에 참조할 수 없다.

console.log(Player);

class Player {}

결과는 다음과 같다.

클래스 선언문도 변수 선언, 함수 정의와 마찬가지로 호이스팅이 발생하는데, 주의점으로 클래스는 let, const 키워드로 선언한 변수처럼 호이스팅된다.

var, let, const, function, class 등의 키워드를 사용해서 선언된 모든 식별자는 호이스팅이 되는데 그 이유는 모든 선언문은 런타임 이전에 먼저 실행되기 때문이다.


[참고자료]

https://ko.javascript.info/class

https://ko.javascript.info/constructor-new

https://www.zerocho.com/category/JavaScript/post/573c2acf91575c17008ad2fc

https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Details_of_the_Object_Model

https://blinders.tistory.com/90

https://velog.io/@dasol-jeong/%EC%83%9D%EC%84%B1%EC%9E%90-%ED%95%A8%EC%88%98-vs-%ED%81%B4%EB%9E%98%EC%8A%A4

profile
https://big-huni.tistory.com // 이전합니다.

2개의 댓글

comment-user-thumbnail
2022년 2월 18일

스터디 계속 하고 계시는 군요! 화이팅입니다~

1개의 답글