클래스
클래스와 프로토타입에 대해 이해합니다

목표
프로토타입과 클래스를 이해합니다.
프로토타입
객체 지향은 클래스 상속을 이용합니다. 자바스크립트도 비슷합니다. 다만 프로토타입이란 방식으로 내부적 구현을 합니다.
const arr = [1,2,3];
arr.map((num) => console.log(num));
.
은 객체의 속성과 메소드를 참조하는 문법입니다. 즉, arr는 객체입니다. map
이라는 메소드를 따로 명시하지 않아도 Array의 프로토타입을 상속받았으므로 Array.prototype.map
메소드를 사용할 수 있습니다.
자바스크립트는 내부적으로 모든 비원시 타입(배열, 함수, 클래스 등)에 프로토타입을 상속시킵니다. 아래와 같은 느낌으로 보면 됩니다.
class Array {
map(){...}
}
const arr = [1,2,3]; // Array의 인스턴스
Object.getPrototypeOf(arr) // arr의 프로토타입은 Array
arr.map() // Array의 map
원시 타입과 프로토타입
// 문자열은 원시 타입이지만 객체처럼 메소드를 사용할 수 있습니다.
"string".charAt("s");
문자열은 원시값이지만 객체처럼 메소드를 참조할 수 있습니다. 자바스크립트는 문자열에서 메소드를 사용할 경우 일시적으로 String의 인스턴스로 변환합니다. 이를 래퍼 객체라고도 부릅니다. 문자열은 런타임에만 객체로서 평가되어 메소드를 참조하고, 코드 평가가 끝나면 다시 문자열로 돌아갑니다.
프로토타입 체인
생성자 함수는 자신이 상속받은 프로토타입에 프로퍼티와 메소드를 추가할 수도 있습니다. 예를 들면 일반 배열은 Array의 프로토타입을 상속받고, Array는 Object의 프로토타입을 상속받습니다. 이처럼 프로토타입 상속은 여러 단계로 상속됩니다. 이를 프로토타입 체인이라 합니다. 참고로 모든 프로토타입의 최종적인 부모는 Object이고, 이 까닭에 자바스크립트는 원시값을 제외한 모든 게 객체라는 말을 합니다.
Object 프로토타입 { ... }
Array 프로토타입 { ... } // Object 프로토타입을 상속받습니다.
const array = [1,2,3]; // Array 프로토타입을 상속받습니다.
__proto__
프로퍼티나, 혹은 Object.getPrototypeOf
로 해당 객체의 부모 프로토타입을 확인할 수 있습니다.
class Person { ... }
const person = new Person();
const proto = Object.getPrototypeOf(person); // Person.prototype
const proto2 = person.__proto__;
생성자 함수
자바스크립트에선 객체를 많이 씁니다. 비슷한 역할을 가진 객체를 대량 만들거나, 특수한 목적을 가진 객체를 만들어야 할 때도 있습니다.
function greet(name) {
console.log(`Hello ${name}`);
}
const user = { name: "user1" };
greet(user.name);
위처럼 단순한 케이스는 함수와 객체를 분리시켜도 무관합니다. 하지만 유저 정보와 관련 함수가 많아지면 중복과 복잡도가 늘어납니다. 그래서 아래와 같이 생성자 함수를 사용합니다.
function User(name) {
this.name = name;
this.greet = function() { ... }; // 인스턴스마다 제각각 생성합니다.
}
User.prototype.greet = function () { ... }; // 모든 인스턴스가 공유합니다.
const user1 = new User("Lee");
const user2 = new User("Kim");
console.log(user1.greet === user2.greet); // true
greet 함수는 항상 기능이 일정하기 때문에 여러 번 생성할 필요없습니다. 모든 유저가 똑같은 greet 함수를 사용해야 효율적입니다. 위와 같이 프로토타입을 통해 함수를 등록하면 모든 인스턴스가 하나의 greet 함수를 공유합니다.
요즘은 위와 같이 프로토타입을 작성하는 대신 클래스를 이용합니다. 클래스는 내부적으로 프로토타입을 쓰기 때문에 코드가 간결하고 일반 생성자 함수보다 많은 기능을 지원합니다.
클래스
class User {
constructor(name) {
this.name = name;
// return this;
}
greet() {
console.log(this.name);
}
}
const user = new User("user1");
// ...
const user = new User("user9547272");
클래스도 생성자 함수입니다. new 접두사를 붙여 실행 시 constructor가 실행되어 인스턴스를 반환합니다. (constructor는 암묵적으로 this를 반환합니다.) constructor에서 선언하는 this.name
따위는 각 인스턴스들이 개별적으로 가질 속성입니다.
constructor 바깥에 적는 메소드는 프로토타입에 등록되어 모든 인스턴스가 공유합니다. 또한 constructor도 메소드처럼 프로토타입에 등록됩니다.
class User {
constructor(name) {
this.name = name
}
// User의 프로토타입에 greet 메소드 등록
greet() {
console.log(this.name)
}
// User의 인스턴스마다 greet 함수 생성
greet2 = () => {
console.log(this.name)
}
}
const user1 = new User("A");
const user2 = new User("B");
console.log(user1.greet === user2.greet) // true
console.log(user1.greet2 === user2.greet2) // false
단, 메소드를 화살표 함수로 작성하면 프로퍼티로서 평가됩니다.

const user = {
greet() { ... }
};
객체 리터럴에서 작성한 함수도 메소드가 아닌 프로퍼티로 평가됩니다. 객체 리터럴을 생성자 함수로 쓸 일이 없기 때문입니다.
메소드와 함수의 차이를 묻는 질문이 많은데 자바스크립트에 한정하면, 메소드 축약법(method shorthand)으로 정의해 프로토타입에 등록된 함수를 뜻할 때가 많습니다.
클래스의 정적 요소
일반적으로 속성과 메소드는 인스턴스를 생성해야만 참조할 수 있습니다. 하지만 static
을 쓰면 인스턴스 없이 클래스 자체에서 참조합니다.
class User {
static greet() {
console.log("hi");
}
greet2() {
console.log("hello");
}
}
const user = new User();
user.greet2(); // 일반 메소드
User.greet(); // 정적 메소드
인스턴스에서 정적 요소에 접근할 일은 없지만 불가능하진 않습니다. 프로토타입을 통해 부모의 정적 요소를 참조할 수 있습니다.
class Person {
static greet() {
console.log("hi");
}
}
const person = new Person();
const proto = Object.getPrototypeOf(person); // Person.prototype
const proto2 = person.__proto__; // Person.prototype
const proto3 = Person.prototype; // Person.prototype
const proto4 = Person.__proto__; // Function.prototype
proto.constructor.greet(); // hi
proto2.constructor.greet(); // hi
console.log(proto === proto2); // true
console.log(proto2 === proto3); // true
console.log(proto2 === proto4); // false
참조
- 이웅모(2020.09). 모던 자바스크립트 Deep Dive: 자바스크립트의 기본 개념과 동작 원리. 위키북스
- ECMAScript® 2023 Language Specification
- The JavaScript this Keyword
- JavaScript - this Keyword
- Arrow function expressions