JavaScript

생성자 함수와 클래스, 그리고 프로토타입

3jun 2023. 2. 9. 00:20

자바스크립트에는 5가지 객체 생성 방법이 있다.

  • 객체리터럴
  • Object 생성자 함수
  • 생성자 함수
  • Object.create 메서드
  • 클래스(ES6)

객체 리터럴은 직관적이고 간편하게 객체를 생성할 수 있는 생성 방식(표기법)이다. 하지만 객체 리터럴은 각각 하나의 객체만 생성하므로 아래 예시와 같이 동일한 프로퍼티를 갖는 객체를 여러 개 생성해야하는 경우 매번 동일한 프로퍼티를 반복해서 작성해야하므로 비효율 적이다. 

const user = {
    name: 'kim',
    id: 'kimabc'
}

const user2 = {
    name: 'lee',
    id: 'mrlee'
}

생성자 함수와 클래스 

생성자 함수와 클래스는 프로퍼티 구조가 동일한 여러 개의 객체를 간편하게 생성할 수 있다. 

 

모던 자바스크립트 딥다이브, 이웅모

생성자 함수

자바스크립트의 생성자 함수는 클래스 기반의 객체지향언어의 생성자와 달리 형식이 정해져 있는 것이 아니라 일반 함수와 동일한 방법으로 정의된 함수를 new 연산자와 함께 호출하면 생성자 함수, 그렇지 않다면 일반 함수로 동작한다.

function User(name) {
    this.name = name,
    getName() {
    	return this.name;
    }
}

// 생성자 함수
const kim = new User('kim');
// 일반함수
User('kim');

이는 함수 객체가 일반 객체가 가지고 있는 내부 슬롯과 메서드와 함수 객체가 가지고 있는 내부 슬롯과 메서드를 모두 갖고 있기 때문이다. 

함수가 일반 함수로서 호출되면 내부메서드인 [[Call]]이 호출되고, 생성자 함수로서 호출되면 내부 메서드 [[Construct]]가 호출된다.

 

모든 함수 객체는 내부 메서드 [[Call]]을 갖고 있으므로 호출할 수 있다. 즉, callable 하다.

반면 내부메서드 [[Construct]]를 갖고 있지는 않다. Construct 내부메서드를 가진 함수 객체만 생성자 함수로 호출할 수 있다.

즉 callable 이지만 constructor가 아닌 함수객체는 일반함수로서만 호출할 수 있고, callable 이면서 constructor 인 함수객체는 생성자 함수로 호출이 가능하다.

  • constructor : 함수 선언문, 함수 표현식, 클래스(클래스 역시 함수)
  • non-constructor: 메서드(ES6 메서드 축약표현), 화살표 함수

클래스

클래스 역시 함수이며 ES6 이전에 사용되던 프로토타입 기반 패턴을 클래스 기반 패턴처럼 사용할 수 있도록 하는 symantic sugar로 볼 수도 있지만, 클래스는 생성자 함수보다 더 엄격하며 추가적인 기능을 제공하므로 새로운 객체 생성 매커니즘으로 보는 것이 더 합당하다.

  • new 연산자 없이 호출되면 에러가 발생한다.
  • 상속을 지원하는 extends 와 super 키워드를 제공한다.
  • 호이스팅이 발생하지 않는 것처럼 동작한다. (생성자 함수의 경우 함수 선언문으로 정의하면 함수 호이스팅이, 함수 표현식으로 정의하면 변수 호이스팅이 발생)
  • 암묵적으로 strict mode 로 실행된다.
  • constructor, 프로토타입 메서드, 정적 메서드의 프로퍼티 어트리뷰트 [[Enumerable]]의 값이 false 로 열거되지 않는다.

프로토타입

자바스크립트는 어떤 객체를 원형(프로토타입)으로 하여 이를 참조(복제) 함으로써 클래스 기반 언어에서의 상속과 유사한 효과를 가진다. 

달리 말하면, 자바스크립트의 모든 객체는 다른 객체와 프로토타입 관계가 있는데 참조의 원형(프로토타입)이 되는 상위 객체와 단방향 링크드 리스트로 구현된 프로토타입 체인과 연결되어 있다. 프로토타입을 상속받은 하위 객체는 프로토타입 체인을 따라서 상위 객체의 프로퍼티를 자신의 프로퍼티처럼 자유롭게 사용할 수 있다. 

__proto__ 접근자 프로퍼티

기본적으로 [[Prototype]] 내부 슬롯에 직접 접근을 불가능하지만 던더 프로토로 불리는 __proto__ 는 자신의 프로토타입, 즉 [[Prototype]] 내부 슬롯에 간접적으로 접근할 수 있도록 하는 접근자 프로퍼티이다. 

객체는 사용하고자 하는 프로퍼티가 없으면 프로토타입 체인을 따라 프로퍼티를 검색하는데 프로퍼티 검색방향이 한쪽 방향으로만 향하지 않는다면 순환참조가 일어나 프로토타입 체인 종점이 존재하지 않아 무한루프에 빠지게 된다. 따라서 상호참조에 의해 프로토타입 체인이 생성되는 것을 방지하기 위해 프로토타입에 접근하려면 프로퍼티를 사용한다.

 

참고
- 모던 자바스크립트 딥다이브, 이웅모
- 코어 자바스크립트, 정재남
- https://poiemaweb.com/
- https://www.howdy-mj.me/javascript/prototype-and-proto