자바스크립트의 Class에 관해 알아보자

toto9602·2023년 12월 25일
0

Javascript, Typescript

목록 보기
2/2

NestJs 등 node js 기반의 프레임워크를 사용할 때, 자바스크립트 Class 문법을 평소에 많이 사용하였습니다.

Class를 사용할 때마다 '자바스크립트는 프로토타입 기반 언어이기 때문에, Class도 사실 프로토타입 기반'이라거나, 'Class도 결국 함수' 같은 말이 아른아른거렸지만...ㅎㅎ
정작 제대로 아는 내용은 없었기 때문에 본 포스팅을 통해 간단히나마 알아보려 합니다!

참고 자료

모던 Javascript 튜토리얼 (Javascript Info) (클래스와 기본문법, 클래스 상속, 함수의 prototype 프로퍼티 등)
자바스크립트 클래스 문법 - 완벽 가이드
poiemaweb - 클래스

들어가기 전에 : new 연산자와 생성자 함수

해당 부분은 주로 위 참고자료 중 new 연산자와 생성자 함수 글을 참고하였습니다.

자바스크립트에는, 유사한 객체 여러 개를 쉽게 만들기 위한 new 연산자와 생성자 함수라는 개념이 존재합니다.

생성자 함수 (constructor function)

[ 생성자 함수 예시 ]

function User(name) {
  this.name = name;
  this.isAdmin = false;
}

// 이하에서 살펴 볼, Class의 사용 방법과 거의 동일하네요!
const user = new User("유저");

console.log(user.name) // "유저"
console.log(user.isAdmin) // false

생성자 함수는, 기본적으로는 일반 함수와 동일하지만, 아래와 같은 두 가지 관례가 존재합니다.

  1. 함수 이름의 첫 글자는 대문자로 시작할 것.
  2. new 연산자를 붙여 실행할 것.
    → new와 함께 호출하면 내부에서 this가 암시적으로 만들어지고, 마지막엔 this가 반환된다고 합니다!

1,2번의 관례만 봐도 벌써 뭔가 Class의 사용 방법과 비슷한 느낌이 드네요!

다만 앞서 본 2가지 관례 중, 2번째 내용이 살짝 와닿지 않으니..
조금만 더 보도록 하겠습니다!

new 연산자를 사용하여 User 함수를 실행하면, 실행되는 내용은 아래와 같습니다.

[ new User("유저") 호출 시 ]

function User(name) {
  // 1. this = {} // 빈 객체가 만들어집니다. 
  
  // 2. this에 프로퍼티를 추가합니다. 
  this.name = name;
  this.isAdmin = false;
  
  // 3. return this (프로퍼티가 추가된 this가 반환됩니다!)

this가 암시적으로 반환되기 때문에, new 연산자를 호출한 결과값을 User 의 인스턴스로 사용할 수 있었던 것 같습니다!

요 부분까지, new 연산자와 생성자 함수에 대한 간단한 내용을 보았고,
Class로 넘어가 보도록 하겠습니다! 🙇

자바스크립트의 Class란..?

자바스크립트 Class는 함수의 일종이다

제목과 같이, 자바스크립트 Class 또한 함수의 한 종류입니다.

class User {
	...
   	...
}

// Class의 type 또한 function ! 
console.log(typeof User); // function

Class인 User의 type을 확인해 보면 function인 것을 확인할 수 있네요 :)

Class 문법 구조가 하는 일

[ Class 작성 예시 ]

class User {
	constructor(name) { // 생성자 메서드
      this.name = name;
   	}
  	sayHi() {
    	console.log(this.name);
    }
}

위와 같은 형태의 Class가 하는 일은 크게 아래 2가지와 같습니다!

  1. User라는 이름을 가진 함수를 생성합니다.
    → 이때, 함수의 본문은 생성자 메서드인 constructor와 같습니다.
    → 즉, 생성자 메서드(constructor)가 없다면, 본문이 비워진 함수가 생성됩니다 :)

  2. 클래스 내에서 정의한 메서드 (ex. sayHi)를 User.prototype에 저장합니다.

cf. 생성자 메서드 (Constructor) 란?

생성자 메서드(constructor)란,
인스턴스를 생성하고 클래스 필드를 초기화하기 위한 특수한 메서드로 정의됩니다.

constructor의 큰 특징 3가지 정도를 아래에서 간단히 살펴보겠습니다!

  1. constructor 메서드는 이름을 바꿀 수 없고, 한 클래스 내에 오직 한 개만 존재할 수 있습니다.
    [ constructor가 2개 이상일 경우, 에러 발생 예시 ]
class Person {
	constructor(name) {
		this.name = name;
    }
  
  	constructor(name, age) {
		this.age = age;
    }
}

// Uncaught SyntaxError : A class may only have one constructor 

  1. constructor는 생략할 수 있습니다.
    이 경우, 클래스에 constructor () {}를 포함한 것과 동일합니다. 즉, 빈 객체를 생성합니다.
    [ 빈 객체 생성 예시 ]
class Person {
  // constrctor가 명시되지 않은 Class
}

console.log(new Person()); // Person {} => 빈 객체

  1. constructor는 인스턴스의 생성과 동시에 클래스 필드의 생성과 초기화를 실행합니다.
class Person {
	constructor(name, age) {
      // this는 클래스가 생성할 인스턴스로, 시작 시점에서는 빈 객체!
      this.name = name; // 필드를 선언하고, 초기화합니다. 
      this.age = age;
    }
}

cf 2. 함수의 prototype 프로퍼티

** 이 부분에서 언급하고자 하는 prototype 프로퍼티는 생성자 함수의 프로토타입으로,
생성자 함수에 정의된 일반 프로퍼티를 말합니다.
자바스크립트의 [[Prototype]]과 다른 개념으로, 이에 대해서는 추후 다른 포스팅을 통해 학습해 보겠습니다 :)

역시 큰 특징 위주로, 간단히 살펴 보겠습니다. :)

1. 함수의 기본 프로퍼티입니다.
[ 별도 정의가 없는 함수 예시 ]

function Person() {}

console.log(Person.prototype); // { constructor : function Person }
  1. 별도 정의가 없다면, constructor 프로퍼티 하나만을 가지며,
    여기서 constructor 프로퍼티는 곧 함수 자신을 다시 가리킵니다.
function Person() {}

console.log(Person.prototype.constructor === Person) // true

생성자 함수와 Class의 비교

표현 방식

Class

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

생성자 함수

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

// 함수의 prototype 프로퍼티에, 정의할 메서드를 추가합니다.
User.prototype.sayHi = function() {
  console.log(this.name);
};

사용 방식

[ Class ]

const user = new User("클래스 방식");
user.sayHi(); // 클래스 방식

[ 생성자 함수 ]

const user = new User("생성자 함수 방식");
user.sayHi(); // 생성자 함수 방식

표현 방식과, 사용 방식을 보았을 때, 기능을 표현하는 방식은 다르지만
클래스 역할을 하는 함수를 선언하는 방법과 Class 키워드를 사용하는 방법의 결과는 거의 같은 모습을 볼 수 있었습니다!

위와 같은 이유로, Class는 기존 문법에 대한 편의 문법(혹은 Syntactic Sugar, 문법 설탕ㅎㅎ)이라고 논의되기도 한다고 합니다!

하지만, 두 방식엔 몇 가지 차이점 역시 존재한다고 하는데요,
본 포스팅에서는 2가지 주요한 차이점 정도를 우선 살펴보려 합니다!

생성자 함수와 Class의 차이점

[[IsClassConstructor]] 프로퍼티

Class 로 만든 함수에는, 특수 내부 프로퍼티인 [[IsClassConstructor]] : true가 붙게 됩니다.
자바스크립트는 이 프로퍼티를 다양하게 활용하는데, 몇 가지 예시는 아래와 같습니다.

1. 클래스 생성자를 new와 호출하지 않으면 에러를 throw합니다.

class User {}

User(); // Class constructor User cannot be invoked without 'new'

/////
function User(name) {
	this.name = name;
}

User("생성자 함수 테스트"); // 에러가 나지 않음!

2. 클래스 생성자를 문자열로 변환하면 class...로 시작하는 문자열이 되도록 합니다.

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

console.log(User); // class User { ... }

function User(name) {
	this.name = name;
}
console.log(User) // f UserB(name) { ... }

메서드의 enumerable 속성

클래스에 정의된 메서드는, 열거할 수 없습니다.
즉, 클래스의 prototype 프로퍼티에 추가된 메서드의 enumerable 플래그는 false 값을 가집니다.

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

console.log(Object.getOwnPropertyDescriptors(User.prototype))
// constructor { ... }
// sayHi : {writable: true, enumerable: false, configurable: true, value : f}


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

User.prototype.sayHi = function() {
  console.log(this.name);
};

console.log(Object.getOwnPropertyDescriptors(User.protytype));
// constructor { ... }
// sayHi : {writable: true, enumerable : true, configurable:true, value: f }

여기까지, 생성자 함수와 자바스크립트의 Class 문법,
그리고 둘 간의 몇 가지 비교를 간단히 짚어보았습니다!

글을 진행하다 보니, 생성자 함수의 비중이 조금 높아진 느낌이지만..
그래도 원래 글의 의도는 Class였으니...ㅎ

자바스크립트의 Class에서도 많이 사용되는 클래스 상속(Class Inheritance)에 대한 내용까지 추가로 살펴보고, 이번 글은 우선 마무리하려 합니다!

cf. 클래스 상속

클래스 상속(Class Inheritance)는, 한 클래스의 기능을 다른 클래스에서 재사용하기 위해 사용하게 됩니다!

자바스크립트에서의 클래스 상속은, 위에서 계속 살펴 본 것과 유사하게
내부적으로 프로토타입 상속 기능을 활용한다고 합니다!

클래스 상속, 프로토타입 상속 문법 비교

클래스 상속

class Person {
	constructor(name, first, second) {
    	this.name = name;
    	this.first = first;
    	this.second = second;
    }
  
  sum() {
  	return (this.first + this.second)
  };
}

class Student extends Person {
  constructor(name, first, second, third) {
  	super(name, first, second);
  	this.third = third;
  }
  
  sum() {
  	return super.sum() + this.third;
  }
  
  avg() {
  	return (this.first + this.second + this.third) / 3;
  }
}

const studentA = new Student("KIM", 10, 20, 30);

위와 같은, 일반적인 클래스 상속 방식을
생성자 함수를 활용한 프로토타입 방식으로 유사하게 구현하면 아래와 같습니다!

프로토타입 상속 방식

// 생성자 함수
function Person(name, first, second) {
	this.name = name;
	this.first = first;
	this.second = second;
}

// 프로토타입에 함수 정의 
Person.prototype.sum = function() {
	return (this.first + this.second);
}

function Student(name, first, second, third) {
  	// 클래스에서 super() 같은 역할을 하는 부분!
  	// 생성자 함수 Person을 호출합니다. 
  	// 호출 시에, this를 bind하여 call을 호출!
	Person.call(this, name, first, second);
  	this.third = third;
  	// 여기까지 해도, 아직 상속이 된 것은 아님!
}

// 방법 1
//Student 프로토타입 오브젝트 링크를 Person 프로토타입 오브젝트로 연결시켜 sum을 사용할수있게 찾아가도록 설정
student.prototype.__proto__ = Person.prototype;

// 방법 2
// Person 프로토타입 object를 새로 만들어서 대입
// Student의 prototype 프로퍼티에 Person prototype으로 만든 객체를 할당
Student.prototype = Object.create(Person.prototype);

Student.prototype.constructor = Student; // 생성자를 연결

// Student의 함수를 prototype 프로퍼티에 할당
Student.prototype.avg = function() {
	return (this.first+this.second+this.third) / 3;
}

const studentB = new Student("LEE", 10, 20, 30);

정리

  • 자바스크립트의 Class는, 함수의 한 종류이다!
  • Class는 동일한 이름을 가진 함수를 생성하고, Class 내에서 정의한 메서드를 해당 함수의 prototype 프로퍼티에 저장하는 방식으로 작동한다!
  • 생성자 함수와 Class는 비슷하게 사용되지만, 기능상 차이가 있으므로
    Class를 그저 편의 문법(Syntactic Sugar)로 보기는 어렵다.
  • Class의 상속 역시, 프로토타입 기반!

본 포스팅을 통해, 자바스크립트의 Class에 대해 조금 더 이해해 보고자 하였습니다!
사실 제대로 이해하려면, 프로토타입 개념에 대한 조금 더 깊이 있는 이해가 필요할 것 같다는 점을 많이 느꼈지만...ㅎ
그래도 Class에 대한 대략적인 이해 정도를 한 것으로 오늘은 이만 만족하고,
다음 포스팅을 기약하겠습니다! :)

profile
주니어 백엔드 개발자입니다! 조용한 시간에 읽고 쓰는 것을 좋아합니다 :)

0개의 댓글