ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 코어 자바스크립트 - 프로토타입
    코어 자바스크립트 2025. 6. 29. 13:10

     

    프로토타입 개념

    자바스크립트는 프로토타입 기반 언어이다. 어떤 객체를 원형(prototype)으로 삼고 참조하여 상속과 비슷한 효과를 얻는다.

     

    프로토타입 도식

     

    어떤 Constructor(생성자 함수)를 new 연산자와 함께 호출하면, Constructor에서 정의된 내용을 바탕으로 새로운 instance가 생성된다. 

    이 때 instance에는 __proto__라는 프로퍼티가 자동으로 부여되는데, 이 프로퍼티는 Constructor의 prototype이라는 프로퍼티를 참조한다.

     

     

    var Person = function (name) {
      this._name = name;
    };
    
    Person.prototype.getName = function () {
      return this._name;
    }
    
    var suzi = new Person('Suzi');
    
    Person.prototype === suzi.__proto__ // true
    Person.prototype === Object.getPrototypeOf(suzi) // true
    
    // 브라우저가 아닌 환경에서 suzi.__proto__와 같은 방식이 브라우저가 아닌 환경에서 지원되지 않을 가능성이
    // 있어서 Object.getPrototypeOf()를 이용하도록 하자.

     

    Person이라는 생성자 함수를 통해 객체를 생성하기 위한 틀을 만들었다. Person 생성자 함수의 prototype에 getName 메서드를 지정한 상태이다. 이제 Person의 인스턴스는 __proto__ 프로퍼티를 통해 getName을 호출 할 수 있다.

     

     

    suzi.__proto__.getName(); // undefined

     

    예상과 다르게 메서드 호출 결과로 undefinded가 나왔다. 에러가 발생하지 않은 것을 보면 this에 바인딩 된 대상이 잘못 지정되었다는 뜻이다. 함수 내부에서 this는 'suzi'가 아니라 'suzi.__proto__' 객체를 바라보고 있다. 'suzi'를 바라보게 하기 위한 방법은 __proto__를 생략하면 된다.

     

     

    suzi.getName(); // 'Suzi'

     

    다시 정리하면 __proto__ 프로퍼티는 생략 가능하도록 구현되어 있기 때문에 생성자 함수의 prototype에 어떤 메서드나 프로퍼티가 있다면 instance에서도 마치 자신의 것처럼 해당 메서드나 프로퍼티에 접근할 수 있게 된다.

     

     

     

     

     

     

    이번에는 대표적인 내장 생성자 함수인 Array를 크롬 개발자 도구로 확인해보자.

     

    var arr = [1, 2];
    
    console.dir(arr);
    console.dir(Array);

     

    왼쪽 console.dir(arr) / 오른쪽 console.dir(Array)

     

    왼쪽 arr 변수 출력 결과를 보면 첫 줄에는 Array(2)라고 표기되어 있다. Array라는 생성자 함수를 원형으로 삼아 생성된 것을 알 수 있다.

    __proto__([[Prototype]])을 열어보면 다양한 메서드들이 있다. 

     

    오른쪽 Array를 보면 Array 함수의 정적 메서드인 from, isArray등이 보이고, prototype를 열어보면 왼쪽의 __proto__와 완전히 동일한 내용으로 구성돼 있다. 

     

    __proto__는 Array.prototype를 참조하는데, __proto__가 생략 가능하도록 설계돼 있기 때문에 instance가 push, pop, forEach 등의 메서드를 마치 자신의 것처럼 호출할 수 있다. 한편 Array의 prototype 프로퍼티 내부에 있지 않은 from, isArray 등의 메서드들은 instance가 직접 호출할 수 없다.

     

    var arr = [1, 2];
    
    Array.isArray(arr); // true
    arr.isArray(); // TypeError

     

     

     

     

    - constructor 프로퍼티

     

    생성자 함수의 프로퍼티인 prototype 객체 내부에는 constructor라는 프로퍼티가 있다. 인스턴스의 __proto__ 객체 내부에도 마찬가지다. 이 프로퍼티는 단어 그대로 원래의 생성자 함수(자기 자신)을 참조한다.

     

    var arr = [1, 2];
    Array.prototype.constructor === Array // true
    arr.__proto__.constructor === Array // true
    arr.constructor === Array // true

     

     

     

     

     

     

     

    프로토타입 체인

    - 메서드 오버라이드

    만약 instance가 prototype에 정의된 것과 동일한 이름의 프로퍼티 또는 메서드를 가지고 있으면 어떻게 될까?

     

    var Person = function (name) {
      this.name = name;
    };
    Person.prototype.getName = function () {
      return this.name;
    };
    
    var iu = new Person('지금');
    iu.getName = function () {
      return '바로' + this.name;
    };
    
    console.log(iu.getName()); // 바로 지금

     

    iu._proto__.getName이 아닌 iu 객체에 있는 getName 메서드가 호출되었다. 여기서 일어난 현상을 메서드 오버라이드라고 한다. 원본을 제거하고 다른 대상으로 교체하는 것이 아니라 원본이 그대로 있는 상태에서 다른 대상을 그 위 얹었다고 생각하면 된다.

     

    자바스크립트 엔진은 가장 가까운 대상인 자신의 프로퍼티를 검색하고, 없으면 그 다음으로 가까운 대상인 __proto__를 검색하는 순서로 진행된다. 그러니까 __proto__에 있는 메서드는 자신에게 있는 메서드보다 검색 순서에 밀려 호출되지 않은 것이다.

     

     

     

     

     

    - 프로토 타입 체인

    console.dir({ a: 1 });
    
    console.dir([1, 2]);

     

     

    왼쪽 { a: 1 } / 오른쪽 [1, 2)

     

    오른쪽 배열 리터럴을 보면 __proto__에는 pop,push가 있다고 했다. 추가로, 이 __proto__ 안에는 또다시 __proto__가 등장한다. 왼쪽의 객체와 동일한 내용으로 이뤄져있다. 동일한 이유는 바로 prototype 객체도 '객체'이기 때문이다. 

     

    기본적으로 모든 객체의 __proto__ 는 Object.prototype가 연결된다.

    Object.prototype => Array.prototype => [1, 2]

     

    어떤 데이터의 __proto__ 프로퍼티 내부에 다시 __proto__ 프로퍼티가 연쇄적으로 이어진 것을 프로토타입 체인이라 하고, 이 체인을 따라가며 검색하는 것을 프로토타입 체이닝이라고 한다. 어떤 생성자 함수이든 prototype은 반드시 객체이기 때문에 Object.prototype는 언제나 프로토타입 최상단에 존재하게 된다.

     

     

     

     

    - 객체 전용 메서드의 예외사항

    모든 생성자 함수가 프로토 타입 체이닝을 통해 Object.prototype에 접근이 가능하기 때문에 부득이 하게 객체 전용 메서드들은 Object staitc 메서드로 부여할 수 밖에 없었다.

     

    // 객체 전용 메서드 예시
    var obj = { a: 1 };
    
    Object.assign({}, obj);
    Object.keys(obj);
    Object.entries(obj);
    Object.create(proto);
    
    
    obj.freeze(); // X TypeError
    Object.freeze(obj); // O { a: 1 }

     

     

     

     

     

     

    - 다중 프로토타입 체인

    __proto__로 프로토 타입 체인을 연결해나가기만 하면 무한대로 체인 관계를 이어나갈 수 있다. __proto__ 연결하는 방법은 __proto__가 가리키는 대상을 상위 생성자 함수의 instance를 바라보게끔 해주면 된다.

     

    var Grade = function () {
      var args = Array.prototype.slice.call(arguments);
      for (var i = 0; i < args.length; i++) {
        this[i] = args[i];
      }
      this.length = args.length;
    };
    var g = new Grade(100, 80);
    
    console.log(g) // Grade {0: 100, 1: 80, length: 2}

      

    위 코드는 생성자 함수로 유사배열객체를 만든 코드이다. 유사배열객체는 배열 메서드를 사용하지 못하지만 배열 메서드를 직접 쓰게끔 만들고 싶다. 그러기 위해서는 g.__proto__, 즉 Grade.prototype이 배열의 인스턴스를 바라보게 하면 된다.

     

     

    var Grade = function () {
        var args = Array.prototype.slice.call(arguments);
        for (var i = 0; i < args.length; i++) {
            this[i] = args[i];
        }
        this.length = args.length;
    };
    
    Grade.prototype = []; // 인스턴스 만들기 전에 배열 바라보게
    
    var g = new Grade(100, 80);
    g.push(90); 
    console.log(g); // Grade {0: 100, 1: 80, 2: 90, length: 3}

      

    Grade.prototype = [] 이 명령에 의해 서로 별개로 분리돼 있던 데이터가 연결되어 하나의 프로토타입 체인 형태를 띄게 된다.

     

    - 기존 코드 : Object.prototype => Grade.prototype => g {0: 100, 1: 80, length: 2}
    - 수정 코드 : Object.prototype => Array.prototype => Grade.prototype [] => g {0: 100, 1: 80, length: 2}

     

     

    이러한 ES5 체계에서의 생성자 함수 및 프로토타입의 상속 및 추상화 방법들은 ES6의 클래스 문법(extends, super)에서 상당히 간단하게 처리가 된다.

     

     

     

     

     

     

     

     

     

    코어 자바스크립트를 읽고 정리한 글입니다.

     

    https://product.kyobobook.co.kr/detail/S000001766397

     

    코어 자바스크립트 | 정재남 - 교보문고

    코어 자바스크립트 | 자바스크립트의 근간을 이루는 핵심 이론들을 정확하게 이해하는 것을 목표로 합니다!최근 웹 개발 진영은 빠르게 발전하고 있으며, 그 중심에는 자바스크립트가 있다고

    product.kyobobook.co.kr

     

     

     

     

     

    댓글

Designed by Tistory.