
자바스크립트는 독특하게 클래스 기반과 함수형 둘 다 지원하는 유연한 언어입니다. 이 문서에서는 클래스, 프로토타입, 생성자 함수 등을 친절하게 설명하고, 클래스와 함수형 방식 비교도 함께 다룰게요!
Java, Python, Ruby 같은 언어들은 전통적인 클래스 기반 객체지향 언어입니다.
비유하자면:
예를 들어 Java에서는:
class Person {
String name;
int age;
void introduce() {
System.out.println("안녕하세요");
}
}
이렇게 class로 설계도를 만들고, 그걸로 객체(인스턴스)를 만들죠.
자바스크립트도 객체지향 언어예요. 다만 원래는 클래스가 없고, 대신 프로토타입(Prototype) 이라는 독특한 방식으로 객체 간 상속을 구현했어요.
📌 ES6 이후에는 class 키워드가 도입되어 문법이 더 익숙하게 바뀌었지만, 내부적으로는 여전히 프로토타입 기반으로 동작해요.
프로토타입은 객체가 물려받는 원형(DNA)입니다. 객체가 가진 속성이나 메서드가 없다면, 자바스크립트는 상위 원형(프로토타입)을 따라가며 찾습니다. 이것을 **프로토타입 체인(Prototype Chain)**이라고 해요.
예:
const arr = [1, 2, 3];
arr.sort(); // sort는 어디서 왔을까?
배열은 Array.prototype이라는 틀을 상속받고 있고, 그 안에 sort, push 등의 메서드가 정의돼 있어서 쓸 수 있는 거예요.
new 키워드를 이용하면 생성자 함수를 통해 객체를 만들 수 있어요.
function User(name, age) {
this.name = name;
this.age = age;
}
const u1 = new User("채원", 25);
User는 객체 설계도 역할을 해요.new를 붙이면 새로운 객체가 만들어지고 this는 그 객체를 가리켜요.User.prototype에 메서드를 추가하면, 모든 인스턴스에서 공유할 수 있어요.
prototype: 함수(특히 생성자 함수)에 붙는 속성으로, 인스턴스들이 참조하는 메서드나 속성을 정의하는 공간입니다.__proto__: 실제 인스턴스 객체가 갖고 있는 숨겨진 링크. 자신의 부모 prototype을 참조합니다.📌 __proto__는 오래된 방식이므로 Object.getPrototypeOf() 같은 표준 메서드를 사용하는 게 좋습니다.
const arr = [10, 20];
arr.toString(); // 어디서 왔을까?
function Person() {}
Person.prototype.eyes = 2;
Person.prototype.nose = 1;
const kim = new Person();
const park = new Person();
prototype에 정의하면 한 번만 저장되고, 모든 객체가 공유해서 메모리 효율적입니다.class User {
constructor(name, age) {
this.name = name;
this.age = age;
}
introduce() {
return `안녕하세요, 저는 ${this.name}이고 ${this.age}살입니다.`;
}
}
const user1 = new User("채원", 25);
console.log(user1.introduce());
const createUser = (name, age) => {
const introduce = () => `안녕하세요, 저는 ${name}이고 ${age}살입니다.`;
return { name, age, introduce };
};
const user2 = createUser("채원", 25);
console.log(user2.introduce());
| 항목 | 클래스 방식 | 함수형 방식 |
|---|---|---|
| 구조 | 상태 + 메서드 결합 | 함수로 객체 구성 |
| 메서드 저장 위치 | prototype 공유 | 매번 새로 생성됨 |
| 메모리 효율 | 좋음 | 상대적으로 떨어짐 |
| 복잡한 앱 | 유리함 | 불리함 (확장 어려움) |
| 간단한 구조 | 과할 수 있음 | 간결하고 빠름 |
화살표 함수는 일반 함수와는 다르게 this를 자신이 정의된 시점의 외부 스코프에서 고정해 버립니다.
여기서 말하는 "외부 스코프"는 화살표 함수가 만들어질 때 감싸고 있는 함수나 클래스, 혹은 전역 객체를 의미합니다.
예를 들어, 클래스의 constructor 안에서 화살표 함수를 만들면, 그 화살표 함수는 constructor 안의 this—즉 해당 클래스 인스턴스를 기억하게 됩니다.
일반 함수는 누가 호출하느냐에 따라
this가 바뀌지만, 화살표 함수는 "나는 처음 만들어졌을 때의this만 쓸 거야!"라고 고정해 버리는 거예요.
class Counter {
constructor() {
this.count = 0;
setInterval(function () {
this.count++; // ❌ 여기서 this는 Counter 인스턴스가 아님
}, 1000);
}
}
이 경우 일반 함수는 setInterval 내부에서 this가 window 또는 undefined를 가리키게 되어 this.count는 작동하지 않습니다.
화살표 함수로 바꾸면:
class Counter {
constructor() {
this.count = 0;
setInterval(() => {
this.count++; // ✅ this는 Counter 인스턴스를 기억함
}, 1000);
}
}
화살표 함수는 생성 시점의 외부 스코프, 즉 constructor의 this를 기억하기 때문에, 이 안에서 this.count++가 잘 작동합니다.
this를 가지지 않고, 만들어질 당시의 외부 this를 기억합니다.this를 잃지 않고 사용할 수 있어요.method() {})로 선언하는 것이 더 적절합니다. 이유는 this를 고정시키지 않고 동적으로 다룰 수 있기 때문입니다.화살표 함수는 일반 함수와는 다르게 this를 자신이 정의된 시점의 외부 스코프에서 고정해 버립니다. 즉, 누가 호출하느냐에 따라 this가 바뀌는 일반 함수와 달리, 화살표 함수는 항상 태어난 환경의 this만 참조합니다.
class Counter {
constructor() {
this.count = 0;
setInterval(function () {
this.count++; // ❌ this는 Counter 인스턴스가 아님
}, 1000);
}
}
이 경우 this는 setInterval의 내부 컨텍스트를 가리키므로 this.count는 undefined가 됩니다.
같은 코드를 화살표 함수로 바꾸면:
class Counter {
constructor() {
this.count = 0;
setInterval(() => {
this.count++; // ✅ this는 Counter 인스턴스를 기억함
}, 1000);
}
}
이제는 this가 Counter 인스턴스를 올바르게 가리켜 원하는 대로 동작합니다.
this를 가지지 않고, 외부 스코프의 this를 캡처합니다.this를 유지해야 할 때 유용합니다.| 개념 | 설명 |
|---|---|
| 클래스 | 객체를 만들기 위한 설계도 (ES6 이후 문법 도입) |
| 생성자 함수 | class 없이 객체 만드는 방법 (전통적인 방식) |
| 프로토타입 | 상속과 메서드 공유를 위한 구조 |
| 프로토타입 체인 | 속성 없을 때 상위 prototype에서 계속 탐색 |
| 클래스 vs 함수형 | 상황에 따라 선택: 복잡함 ↔ 간결함 |
💡 결론: 자바스크립트는 객체지향도 가능하고 함수형도 가능해요.
필요에 따라 둘 중 하나를 선택하거나, 섞어서 사용하면 됩니다! 😄