서론
Javascript에서 객체 상속은 prototype을 이용해 이루어진다. prototype은 엄밀히 말하면 전통적인 상속이 아닌 참조 메커니즘으로 작동한다. prototype 언어의 특성상, 대부분의 구성요소가 객체로 취급되며, 이는 함수, 배열, 심지어 기본 자료형까지 포함한다.
ES6에서 도입된 클래스 문법은 prototype 기반의 동작을 더욱 쉽게 만드는 문법적인 설탕이다. 따라서, prototype을 이해하는 것은 자바스크립트를 효과적으로 다루는 데 중요하다.
본 글에서는 prototype과 prototype 체인의 개념 그리고 생성자 함수에 대해 설명하고자 한다. 이를 통해 자바스크립트의 객체 지향적 접근 방식을 더 깊이 이해할 수 있길 바란다.
프로토타입과 프로토타입 체인
prototype은 다른 객체에 대한 참조 링크를 보유하는 비공개 속성으로, 객체가 다른 객체의 속성과 메소드를 상속받는 방식을 정의한다. 그림1에서 instance는 constructor에 대한 링크를 보유하고 있음을 볼 수 있다. 생성자인 Constructor도 prototype 객체이고 자신만의 프로토타입을 가지고 있으며, prototype으로 null을 가진 객체에 도달할 때까지 이 연결은 계속된다.
이해를 돕기 위해, 생성자 함수를 사용하여 객체를 생성하는 과정을 살펴보자.
function Food(name, price) {
this.name = name;
this.price = price;
}
let pasta = new Food("pasta", 10000);
pasta 객체를 콘솔에서 조사하면, 이 객체의 프로토타입인 Food()에 정의된 여러 속성과 메서드를 볼 수 있다.
또한, Food()의 프로토타입 객체인 Object에 정의된 추가적인 멤버들도 확인할 수 있다. 이는 prototype 체인이 작동하는 방식의 실질적인 예시이다.
객체가 특정 메서드를 호출할 때, 자바스크립트 엔진은 먼저 해당 객체에서 메서드를 찾는다. 만약 없다면, 객체의 프로토타입을 검사하고, 그 후에도 없다면 prototype의 prototype을 차례대로 검색한다. 이러한 메커니즘은 pasta.valueOf()와 같은 메서드 호출에서 볼 수 있다. 이 프로세스는 객체의 속성을 찾을 때도 동일하게 적용되며, prototype 체인의 종단에 도달할 때까지 속성을 탐색한다.
프로토타입 속성
자바스크립트에서 객체의 상속은 prototype 속성을 통해 이루어진다. 이 prototype 속성은 객체의 생성자에 의해 정의되며, 상속받을 멤버들이 여기에 포함된다. 예를 들어 그림3의 Array.isArray()나 Array.from()과 같은 메서드들은 prototype에 정의되지 않아 상속되지 않는다. 반면에, Array.prototype에 정의된 메서드들은 상속되어, 모든 객체가 사용할 수 있다.
프로토타입 속성에는 __proto__와 prototype이 있다. __proto__는 개별 객체의 속성으로, 객체의 프로토타입(즉, 상속받은 객체)을 가리킨다. 반면, prototype은 생성자 함수의 속성으로, 해당 생성자를 통해 생성된 모든 객체가 상속받을 멤버들을 정의한다. 그림3에서 Array를 통해 생성된 객체는 __proto__를 가지고 생성자인 Array는 prototype을 가짐을 볼 수 있다.
__proto__에 정의된 메서드들은 프로토타입 메서드라고 하며, prototype에 정의된 메서드들은 스태틱 메서드라고 한다. 스태틱 메서드는 인스턴스가 직접 호출할 수 없고 생성자 함수를 통해서만 접근 가능하다. 그림3의 from()과 isArray등은 __proto__에 정의된 프로토타입 메서드이고 push()나 pop()같은 메서드들은 프로토타입 메서드이다.
arr = [1,2,3]
arr.isArray() // undefined-isArray()는 스태틱 메서드로 생성자를 통해 접근해야 한다.
Array.isArray(arr) // true
그러면 평소 array.length같은 메서들을 호출할 때 length 앞에 __proto__를 붙이지 않았는데 왜 프로토타입 메서드인지 의아할 수 있다. 왜냐하면 __proto__속성은 생략가능한 속성이라 그렇다. 실제로 array.length와 array.__proto__.length는 동일한 표현이다. 그림1에서 instance의 __proto__가 흐린 글씨로 쓰인 것을 볼 수 있다. 이는 생략 가능함을 의미한다.
프로토타입에 접근할 때 __proto__보다 Object.getPrototypeOf()나 Object.create()를 사용하는 것이 더 권장된다. __proto__를 객체의 키로 사용할 경우 예상치 못한 버그가 발생할 가능성이 있기 때문이다.
생성자 함수
모든 생성자 함수는 constructor 속성을 지닌 객체를 프로토타입 객체로 가지고 있다. 이 constructor 속성은 원본 생성자 함수 자신을 가르킨다.
예를 들어, Food 생성자 함수로 두 객체 pasta와 sandwich를 생성해보자. 두 객체의 constructor 속성은 모두 원본 생성자 함수인 Food을 가리킨다
function Food(name, price) {
this.name = name;
this.price = price;
}
let pasta = new Food("pasta", 10000);
let sandwich = new Food("sandwich", 8000);
console.log(pasta.constructor); // Food()
console.log(sandwich.constructor); // Food()
이러한 constructor 속성의 중요성은 객체가 어떤 생성자 함수로부터 생성되었는지를 식별하는 데 있다. 이는 특히 프로토타입을 통한 상속 구조에서 중요하며 객체의 유형을 확인하고 적절한 방식으로 처리하는 데 활용된다. 외부 라이브러리를 사용할 때 더욱 유용한데 객체를 사용하다 이 객체가 무엇으로 만들어졌는지 쉽게 파악할 수 있다.
또한 constructor 속성은 객체의 기능을 확장하거나 수정할 때 해당 객체의 원본 생성자 함수에 접근할 수 있는 경로를 제공한다. 이를 통해 프로토타입 체인 내에서 객체의 생성과 상속 메커니즘이 보다 명확하게 이해될 수 있다.
따라서 constructor 속성으로 객체의 출처와 형태를 파악하고, 상속 관계를 명확히 할 수 있다.
참고자료
정재남. (2019). 코어 자바스크립트. 위키북스.
객체의 프로토타입 - 자바스크립트: 객체 프로토타입에 대한 설명
자바스크립트에서의 상속과 프로토타입 체인: 상속과 프로토타입 체인에 대한 상세 정보
프로토타입 상속 - Javascript.info: 프로토타입 상속에 대한 상세 설명
'JS' 카테고리의 다른 글
[JS] Javascript에서의 OOP (0) | 2024.07.22 |
---|---|
[JS] Javascript에서의 Memory Management (0) | 2024.07.18 |
[JS] Javascript에서의 Closures (1) | 2024.01.11 |
[JS] Javascript에서의 this (1) | 2024.01.09 |
[JS] 반복문 대신 고차함수를 쓰는 이유 (0) | 2024.01.07 |