프로토타입

자바스크립트 : 명령형, 함수형, 프로토타입 기반 객체 지향 프로그래밍을 지원하는 멀티 패러다임 프로그래밍 언어

자바스크립트는 클래스 기반 객체지향 프로그래밍 언어보다 효율 적인 프로토타입 기반의 객체지향 프로그래밍 언어

자바스크립트는 객체 기반 프로그래밍 언어이며, 자바스크립트를 이루는 거의 모든 것이 객체이다.

원시 타입의 값을 제외한 함수, 배열, 정규 표현식 등은 모두 객체이다.

객체 지향 프로그래밍

여러 개의 독립적 단위인 객체의 집합으로 프로그램을 표현하려는 프로그래밍 페러다임

실제 세계에는 사물이 존재하며 해당 사물은 특징이나 성지을 나타내는 속성을 지녀, 이를 바탕으로 사물을 인식하거나 구별할 수 있습니다.

프로그래밍에서는 특정 사람의 다양한 속성 중에서 이름과 주소처럼, 필요한 속성을 간추려 내어 표현하는 것을 추상화라고 합니다.

이처럼 속성을 통해 여러 개의 값을 하나의 단위로 구성한 복합적인 자료구조를 객체라고 합니다.

객체 지향 프로그래밍은 객체의 상태를 나타내는 데이터와 상태 데이터를 조작할 수 있는 동작 데이터를 묶어 생각합니다.

객체의 상태 데이터를 프로퍼티

동작을 메서드 라고 부릅니다.

각각의 객체들은 독립적인 부품일 수 있지만, 다른 객체와 관계성을 가질 수 있습니다.

  • 다른 객체와 데이터를 주고 받거나 처리할 수 있습니다.
  • 데이터를 상속받을 수 있습니다.

상속과 프로토타입

  • 상속 : 어떤 객체의 프로퍼티 또는 메서드를 다른 개체가 상속받아 그대로 사용할 수 있는 것
    • 자바스크립트는 프로토타입을 기반으로 상속을 구현하여 불필요한 중복을 제거합니다.

      생성자 함수에 대한 메서드를 각각 만드는 것이 아니라 하나의 메소드를 모든 인스턴스가 공유하는 것이 바람직합니다.

      이를 위하여 프로토타입을 기반으로 상속을 구현합니다.

      function new Circel(radius){
      	this.radius = radius
      }
      
      Circle.prototype.getArea = function(){
      	return Math.PI * this.radius ** 2
      }
      
      const circle1 = new Circle(1)
      circle1.getArea // 3.14...
  • 프로토타입 : 상위 객체, 즉 부모 객체의 역할을 수행합니다.
    • 따라서 생성자 함수가 생성한 모든 인스턴스는 프로토타입의 모든 프로퍼티와 메소드를 상속받습니다.
    • 이를 통해 메소드 및 프로퍼티가 공유되어 코드가 재사용 될 수 있습니다.

포로토타입 객체

  • 프로토타입 객체 : 객체 간 상속을 구현하기 위해 사용
    • 다른 객체에 공유 프로퍼티를 제공합니다.
    • 프로토타입을 상속받은 하위 객체는 상위 객체의 프로퍼티를 자유롭게 이용가능합니다.
  • 모든 객체는 [[prototype]]이라는 내부 슬롯을 지닙니다.
    • 해당 내부 슬롯의 값은 프로토타입의 참조입니다.
    • 여기에 저장되는 프로토타입은 객체 생성 방식에 의해 결정됩니다.
    • 객체가 생성될 때 객체 생성 방식에 따라 결정되며 해당 슬롯에 저장됩니다.
  • 모든 객체는 하나의 프로토타입을 가집니다.
  • 모든 프로토타입은 생성자 함수와 연결되어 있습니다.
    • 객체와 프로토타입과 생성자 함수는 각각각 서로 연결되어 있습니다.
  • [[Prototype]] 내부 슬롯에는 직접 접근이 불가능합니다.
    • **proto** 접근자 프로퍼티는 내부 슬롯이 가리키는 프로토타입에 간접적으로 접근 가능합니다.
  • **proto** 접근자 프로퍼티
    • 모든 객체는 **proto** 접근자 프로퍼티를 통해 내부 슬롯에 간접적으로 접근 가능합니다.
    • **proto** 접근자 프로퍼티는 값 프로퍼티를 지니지 않으며, [[get]]과 [[set]] 프로퍼티 속성으로 구성됩니다.
      • 해당 접근자 함수의 set과 get을 통해 값에 접근하고 새로운 프로퍼티를 할당할 수 있습니다.

        const obj = {}
        const parent = {x : 1}
        
        obj.__proto__ // [[get]]
        obj.__proto__ = parent // [[set]]
        
        obj.x // 1
    • proto 접근자 프로퍼티는 객체가 직접 소유하는 프로퍼티가 아니라 Object.prototype의 프로퍼티입니다.
      • 모든 객체는 상속을 통해 Object.prototype.prototype 접근자 프로퍼티를 사용할 수 있습니다.
  • 프로토타입에 접근하기 위해 접근자 프로퍼티를 사용하는 이유는 상호 참조를 막기 위한 방법입니다.
    • 서로가 자신의 프로토타입이 되는 경우는 불가능합니다.
    • 프로토타입 체인은 단방향 링크드 리스트로 구현되어야 합니다.
  • 접근자 프로퍼티를 코드에서 직접 사용하는 것은 권장되지 않습니다.
    • 프로토타입의 참조를 취득하고 싶은 경우에는 Object.getPrototypeOf 메서드를 사용해야 합니다.
    • 프로토타입을 교체하려는 경우에는 Object.setPrototypeOf 메서드를 사용해야 합니다.

함수 객체의 Prototype 프로퍼티

  • 함수 객체만이 소유하는 prototype 프로퍼티는 생성자 함수가 생성할 인스턴스의 프로토타입을 가리킵니다.
  • 함수 객체는 prototype 프로퍼티를 소유하지만, 일반 객체는 prototype 프로퍼티를 소유하지 않습니다.
  • prototype 프로퍼티 : 생성자 함수가 생성할 객체(인스턴스)의 프로토타입을 가리킵니다.
    • 생성자를 만들지 않는 화살표 함수와 메소드 축약표현 : prototype 프로퍼티를 소유하지 않으며 프로토타입도 생성하지 않습니다.
  • 생성자 함수로 호출하기 위해 정의하지 않은 일반 함수는 prototype 프로퍼티 소유
    • 그러나, 객체를 생성하지 않는 일반 함수의 prototype은 의미가 없습니다.
  • 함수 객체가 지닌 prototype 프로퍼티와 **proto** 접근자 프로퍼티는 동일한 프로토타입을 가리킵니다.

프로토타입의 constructor 프로퍼티와 생성자 함수

  • 모든 프로토타입은 constructor 프로퍼티를 가집니다.
  • 해당 프로퍼티는 prototype 프로퍼티로 자신을 참조하고 있는 생성자 함수를 가리킵니다
    • 해당 연결은 함수 객체가 생성될 때 이루어 집니다.
  • 생성자 함수에 의해 생성된 객체(인스턴스)는 constructor 프로퍼티가 없지만, 인스턴스의 프로토타입에는 constructor 프로퍼티가 있습니다.

리터럴 표기법에 의해 생성된 객체의 생성자 함수와 프로토타입

constructor 프로퍼티가 가리키는 생성자 함수는 인스턴스를 생성한 생성자 함수입니다.

  • 생성자 함수가 아닌 객체 리터럴 혹은 함수 선언 및 표현식을 평가하여 생성한 함수 객체는 생성자 함수로 만들어진 prototype 프로퍼티와 다를 수 있습니다.
  • 리터럴 표기법에 의해 생성된 객체는 가상의 생성자 함수를 가집니다.
  • 프로토타입과 생성자 함수는 단독으로 존재가 불가능하며 언제나 쌍으로 존재합니다.

프로토타입의 생성 시점

프로토타입은 생성자 함수가 생성되는 시점에 더불어 생성됩니다.

생성자 함수 구분

  1. 사용자가 직접 정의한 사용자 정의 생성자 함수
    • constructor를 갖는 함수 객체는 new 연산자와 함께 생성자 함수로서 호출이 가능합니다.
    • 해당 함수의 constructor는 함수 정의가 평가되어 함수 객체를 생성하는 시점에 프로토타입이 생성됩니다.
      • 생성된 프로토타입은 해당 생성자 함수의 prototype 프로퍼티에 바인딩됩니다.
      • 생성된 프로토타입은 constructor 프로퍼티만을 가지는 객체입니다.
      • 해당 프로토타입은 객체이기 때문에 자신의 프로토타입을 가집니다.
        • Object.prototype
      • 생성된 프로토타입의 프로토타입은 언제나 위와 같습니다.
  2. 빌트인 생성자 함수
    • 빌트인 생성자 함수도 일반 함수와 마찬가지로 빌트인 생성자 함수가 생성되는 시점에 프토토타입이 생성됩니다.
    • 생성된 프로토타입은 빌트인 생성자 함수의 prototype 프로퍼티에 바인딩 됩니다.
    • 객체 생성 이전에 생성자 함수와 프로토타입은 이미 객체화 되어 존재합니다.
    • 이후 객체를 생성하면 프로토타입은 생성된 객체의 [[ prototype]] 슬롯에 할당될 뿐입니다.
      • 이로 인해 객체는 프로토타입으로부터 상속받게 됩니다.

객체 생성 방식과 프로토타입의 결정

객체 생성 방식

  1. 객체 리터럴
  2. Object 생성자 함수
  3. 생성자 함수
  4. Object.create 메소드
  5. class
  • 모든 객체는 추상 연산에 의해 생성됩니다.
  • OrdinaryObjectCreate는 필수적으로 자신이 생성할 객체의 프로토타입을 인수로 전달받습니다.
    • 그리고 자신이 생성할 객체에 추가할 프로퍼티 목록을 옵션으로 전달할 수 있습니다.
  • 추상 연산은 빈 객체를 생성합니다.
    • 이후, 객체에 추가할 프로퍼티 목록이 인수로 전달될 경우 프로퍼티를 객체에 추가합니다.
      • 그리고 인수로 전달받은 프로토타입을 자신이 생성한 객체의 프로토타입 슬롯에 할당합니다.
        • 이후, 생성한 객체를 반환합니다.

→ 즉, 프로토타입은 추상 연산에 전달되는 인수에 의해 결정됩니다.

  • 해당 인수는 객체가 생성되는 시점에 객체 생성 방식에 의해 결정됩니다.

객체 리터럴에 의해 생성된 객체의 프로토타입

해당 상황에 추상 연산에 전달되는 프로토타입은 Object.prototype입니다.

즉, 객체 리터럴에 의해 생성되는 객체의 프로토타입은 Object.prototype입니다.

obj 리터럴 객체는 생성자 프로퍼티와 hasOwnProperty 메서드와 같은 것을 소유하지는 않습니다.

  • 하지만 자신의 프로토타입인 Object.prototype의 constructor 프로퍼티와 hasOwnProperty 메서드를 사용할 수 있습니다.
  • 이는 해당 Object.prototype 객체를 상속받았기 때문입니다.

Object 생성자 함수에 의해 생성된 객체의 프로토타입

object 생성자 함수를 인수 없이 호출하면 빈 객체가 생성됩니다.

이때 추상 연산에 전달되는 프로토타입은 Object.prototype입니다.

객체 리터럴과의 차이점은 프로퍼티를 추가하는 방식입니다.

객체 리터럴 방식은 객체 리터럴 내부에 프로퍼티를 추가합니다.

  • 하지만 Object 생성자 함수는 빈 객체를 생성한 이후 프로퍼티를 추가해야 합니다.

생성자 함수에 의해 생성된 객체의 프로토타입

new 연산자와 함께 생성자 함수를 호출하여 인스턴스를 생성하면 추상 연산에 전달되는 프로토타입은 생성자 함수의 prototype 프로퍼티에 바인딩되어있는 객체입니다.

  • 이러한 바인딩된 객체인 프로토타입 객체의 프로퍼티는 constructor 뿐입니다.
    • 따라서 해당 프로토타입에 메소드 및 프로퍼티를 추가하여 사용하게 됩니다.

프로토타입 체인

자바스크립트 객체의 프로퍼티에 접근할 때, 해당 객체에 접근하려는 프로퍼티가 없다면 프로토타입 내부 슬롯의 참조를 따라 자신의 부모 역할을 하는 프로토타입의 프로퍼티를 순차적으로 검색합니다.

프로토타입 체인은 자바스크립트가 객체지향 프로그래밍의 상속을 구현하는 메커니즘입니다.

이는 상속과 프로퍼티 검색을 위한 메커니즘 입니다.

이와 바대로, 프로퍼티가 아닌 식별자는 스코프 체인에서 검색됩니다.

  • 식별자(識別子, identifier) : 어떤 대상을 유일하게 식별 및 구별할 수 있는 이름

스코프 체인은 식별자 검색을 위한 메커니즘 입니다.

스코프 체인과 프로토타입 체인은 서로 협력하여 식별자와 프로퍼티를 검색하는데 사용됩니다.

profile
let David_Oh === UX+Programming

0개의 댓글