클래스 기반의 언어와 프로토타입 기반의 언어
ES6부터 JavaScript에서 class 키워드를 사용하여 클래스를 정의할 수 있지만, C++, C#, Java와 같은 클래스 기반의 언어에서 사용하는 클래스와 100% 동일하게 동작하지 않습니다.
따라서, JavaScript에서 class 키워드를 사용하여 클래스를 정의하는 문법을 이해하기 전에 클래스 기반의 언어와 프로토타입 기반의 언어에 대해 간단하게 구분할 수 있어야 하고 class 키워드가 도입되기 전에 JavaScript에서 클래스를 어떻게 정의했는지 알아야 할 필요가 있습니다.
클래스 기반의 언어는 클래스로 객체의 기본적인 형태와 기능(필드와 메서드)을 정의하고 new 키워드와 생성자로 객체를 생성할 수 있습니다.
다음 예제는 클래스 기반의 언어인 C#에서 클래스를 정의하고 객체를 생성하는 방법입니다.
// CSharpClass라는 이름의 클래스 정의
public class CSharpClass
{
// 필드
private string name;
// 생성자
public CSharpClass(string name)
{
this.name = name;
}
// get 프로퍼티
public string getName()
{
return this.name;
}
// set 프로퍼티
public void setName(string name)
{
this.name = name;
}
// 메서드
public void printValue()
{
Console.WriteLine(this.name);
}
}
class Program
{
static void Main(string[] args)
{
// CSharpClass 클래스의 객체 생성
CSharpClass cSharp = new CSharpClass("Hi");
// 객체를 사용하여 클래스에 정의된 printValue() 메서드 호출
cSharp.printValue();
}
}
[실행 결과]
Hi
클래스 기반의 언어에서는 클래스 정의 후 애플리케이션을 실행하면, 클래스에 정의된 형태와 기능(필드와 메서드)을 변경할 수 없습니다. 즉, 프로그램 실행 도중 CSharpClass 클래스의 printValue() 메서드의 로직을 변경하거나 새로운 필드를 추가할 수 없습니다.
반면에 JavaScript와 같은 프로토타입 기반의 언어는 프로그램 실행 도중 클래스에 정의된 메서드의 로직을 변경할 수 있습니다.(객체에 프로퍼티를 추가하는 것이므로 덮어 씌운다는 의미가 정확하겠네요.)
다음 예제는 JavaScript에서 class 키워드를 사용하여 User 클래스 정의 및 User 클래스의 객체를 생성하고 User 클래스에 정의된 show() 메서드의 로직을 변경합니다.
class User {
show() {
console.log('show!');
}
}
var user = new User();
// User 클래스의 show() 메서드가 무의미함...
user.show = function() {
console.log('Hi');
};
user.show();
[실행 결과]
Hi
클래스 기반의 언어는 프로그램 실행 도중 클래스에 정의된 형태와 기능을 변경할 수 없으므로 프로토타입 기반의 언어보다 정확하게 동작하고 안전하다는 장점이 있지만, 유동적이지 않다는 단점이 존재합니다.
반대로 프로토타입 기반의 언어는 프로그램 실행 도중 클래스에 정의된 메서드의 로직을 변경할 수 있으므로 클래스 기반의 언어보다 유동적이지만, 실행 결과를 예측할 수 없으므로 불안전하다는 단점이 존재합니다.
따라서, JavaScript에서 class 키워드를 사용하여 클래스를 정의해도 클래스 기반의 언어에서 사용하는 클래스와 100% 동일하게 동작하지 않다는 것입니다.
다음은 JavaScript에서 함수를 사용하여 클래스를 정의하고 객체를 생성하는 방법을 설명합니다. 프로토타입과 new 연산자에 대한 이해가 부족하다면, 내용이 어려울 수 있습니다.
함수로 클래스 만드는 방법
JavaScript 함수를 사용하여 클래스를 정의하는 예제입니다.
// 함수로 클래스 정의(클래스이자 생성자 역할을 동시에 함)
function User(name) {
// 필드 정의 및 초기화
this.name = name;
// get 프로퍼티
this.getName = function() {
return this.name;
}
// set 프로퍼티
this.setName = function(name) {
this.name = name;
}
// 메서드
this.show = function() {
console.log(this.name);
}
}
var user = new User('John');
user.show();
[실행 결과]
John
클래스 기반의 언어와 유사하게 User()라는 함수는 클래스이자 생성자 역할을 수행하고 User() 함수 내부에 name이라는 필드와 메서드를 정의하였습니다. User 클래스의 객체는 new 키워드를 사용하여 생성합니다.
이 예제는 정상적으로 실행되지만 getName(), setName(), show() 메서드를 공통적으로 사용하는 것이 아니라 객체마다 사용하고 있으므로 메모리를 불필요하게 사용하며, 원하지 않는 동작을 유발합니다.
var user1 = new User('John');
var user2 = new User('Bob');
var user3 = new User('Jim');
세 개의 객체를 생성했을 때, 그림으로 표현하면 다음과 같습니다.
클래스의 메서드를 정의하는 방법
위에서 보여준 문제를 해결하려면 프로토타입을 활용합니다.
다음 예제는 User 함수 객체의 prototype 프로퍼티에 메서드를 정의하였습니다.
function User(name) {
this.name = name;
}
User.prototype.getName = function() {
return this.name;
};
User.prototype.setName = function(name) {
this.name = name;
};
User.prototype.show = function() {
console.log(this.name);
};
var user1 = new User('John');
var user2 = new User('Bob');
var user3 = new User('Jim');
user1.show();
user2.show();
user3.show();
[실행 결과]
John
Bob
Jim
User 클래스의 객체를 생성하면 메서드를 객체마다 가지는 것이 아니라 프로토타입 체인으로 접근하므로 메모리를 효율적으로 사용합니다. 세 개의 객체를 생성하고 프로토타입을 접근하는 과정을 그림으로 표현하면 다음과 같습니다.
정리
- JavaScript는 프로토타입 기반의 언어이므로 클래스를 정의하면 클래스 기반의 언어와 동일하게 동작하지 않습니다.
- 프로토타입을 활용하여 메서드를 정의하면 객체는 프로토타입 체인으로 메서드를 호출합니다.
'JavaScript > 함수' 카테고리의 다른 글
[JavaScript]화살표 함수를 사용하면 안 되는 경우 (0) | 2022.10.09 |
---|---|
[JavaScript]함수 호이스팅(Function Hoisting) (0) | 2022.07.07 |
[JavaScript]프로토타입의 프로퍼티 (0) | 2022.07.06 |
[JavaScript]프로토타입 확장(prototype extend) (0) | 2022.07.06 |
[JavaScript]함수의 프로토타입(prototype in function) (0) | 2022.07.05 |
댓글