ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [JavaScript] 프로토타입 객체와 prototype, __proto__
    비전공자 공부일기/:: WEB & Front-End 2020. 6. 22. 15:33

    저번 글에서 자바스크립트의 객체 생성방식과 프로토타입에 대해 간단히 알아보았다. ▼

     

    [JavaScript] 자바스크립트의 객체, 프로토타입

    자바스크립틍 자바스크립트에서 객체를 생성하는 방법은 다음 3가지가 있다. 1. 기본 객체(Object() 객체)의 생성자 함수를 이용 2. 객체 리터럴을 이용 3. 생성자 함수 이용 1. 기본객체의 생성자 함

    codingwanee.tistory.com

     

    저번글 핵심요약

     

    자바스크립트에서 객체를 생성하는 3가지 방식

    1. 기본객체의 생성자 함수(Object()
    2. 객체 리터럴 (생성시에 중괄호 안에 속성 부여)
    3. 생성자 함수(new)

     

    프로토타입이란? 객체의 원형

    프로토타입 기반 언어는 객체의 원형인 프로토타입을 이용해 새로운 객체를 만들어내고,

    이 객체는 다시 다른 객체의 원형이 될 수 있어 확장성과 객체지향적 프로그래밍을 지원한다.

    이 때, 어떤 방식으로 객체를 생성하느냐에 따라 프로토타입이 달라진다.

     

     

     

    이번 글에서는 프로토타입에 대해 더 자세히 다뤄보겠다.

     

     

    함수객체와 프로토타입 객체

     

    함수를 생성하면 함수객체 생성과 동시에 prototype이라는 객체도 생성된다.

    단순히 생성 뿐 아니라, 이 두 객체는 서로 이어져 있는 구조이다.

    서로를 참조하며 변수로 접근 및 변경 가능 

     

    함수 객체에서는 프로토타입 객체에 prototype이라는 내부변수로 접근할 수 있고,

    프로토타입 객체에서는 constructor라는 변수로 함수에 접근할 수 있다.

    이렇게 서로를 참조하는 레퍼런스 변수를 통해 두 객체는 접근과 변경이 가능하다.

     

    function TestFunc() {}
    
    TestFunc.prototype.pVal = '프로토타입 값' // 프로토타입에 새로운 속성 추가
    TestFunc.prototype.constructor.cVal = '생성자 값' // 프로토타입에 접근해 constructor 변수로 함수에 접근해 속성 추가
    
    console.log(TestFunc.prototype.pVal) // '프로토타입 값' 출력
    console.log(TestFunc.cVal) // '생성자 값' 출력
    console.log(TestFunc.pVal) // undefined 출력

     

    위의 예제코드를 보면 프로토타입에 새로운 속성을 추가해 pVal에 값을 주었는데도

    콘솔창에 TestFunc.pVal이라고 접근하면 undefined라고 나오는 것을 볼수 있다.

    함수는 프로토타입 객체를 생성하지만, 함수 스스로가 프로토타입으로부터 값을 얻을 순 없다.

    대신 다른 방식으로 접근하게 되는데 바로 new 키워드를 이용해 '인스턴스를 생성'하는 방법이다.

     

     

    프로토타입(원형)과 인스턴스의 prototype

     

    자바스크립트는 객체를 생성할 때 자동적으로 프로토타입 객체(__proto__)가 생성자의 프로토타입 프로퍼티로 설정된다.

    그리고 new 키워드로 함수에서 찍어낸 객체, 즉 인스턴스들이 모두 이 프로토타입을 참조한다.

     

    function TestFunc() {
    	// var this = {};  <-- this 생성
        // return this; <-- this 반환
    }
    
    let testInstance = new TestFunc();

     

    위의 예제에서 new를 하면 this객체가 생성되고 반환되어, testInstance에서 해당 객체를 사용할 수 있게 된다.

    그리고 이 this는 내부변수로 proto를 갖는데 this가 가리키는 프로토타입은 TestFunc의 프로토타입이다.

    또한 TestFunc의 함수객체와는 다르게 TestFunc의 함수에 직접 접근할 수 있는 권한을 가진다.

     

    그러나 내부적으로 해당 프로토타입에 직접 접근을 했다기보다는 값을 복사해서 가져오는 개념이다.

    아래 예제를 보면 prototype이 '원형'의 의미를 갖는 이유를 알 수 있다.

     

    function TestFunc() {}
    
    TestFunc.prototype.pVal = 100; // 프로토타입 속성 설정
    let testInstance = new TestFunc(); // TestFunc의 인스턴스 생성
    console.log(TestFunc.pVal) // 100이 제대로 출력되는 것 확인
    
    testInstance.pVal = 10; // 인스턴스의 pVal 값을 10으로 변경
    console.log(testInstance.__proto__.pVal) // 프로토타입의 pVal 접근. 100이 출력
    console.log(testInstance.pVal) // 인스턴스의 pVal 접근. 10이 출력

     

    즉 인스턴스를 생성할 때 prototype으로부터 참조 가능한 기본값을 가져오지만,

    각각의 인스턴스는 받은 값을 개별로 사용할 수 있다.

     

    그럼 메모리는 어떻게 되어있는지 아래를 보자.

     

     

     

    프로토타입과 메모리

    function TestFunc() {}
    
    // 프로토타입에 속성 설정 후 인스턴스 생성
    TestFunc.prototype.pVal = 100;
    let testInstance = new TestFunc();
    console.log(testInstance.pVal === TestFunc.prototype.pVal) // true
    
    testInstance.pVal = 10; // 인스턴스의 pVal을 10으로 변경
    console.log(testInstance.pVal === TestFunc.prototype.pVal) // false

     

    참고로 자바스크립트에서 == 연산자는 타입과 상관 없이, === 연산자는 타입까지 비교하는 관계연산자이다.

    처음에는 인스턴스의 pVal과 프로토타입의 pVal가 같은지 비교연산이 true라고 나오지만

    인스턴스의 pVal을 변경한 뒤 비교하자 false라고 나온다.

     

    이를 통해 유추하자면,

    프로토타입의 속성을 인스턴스에서 접근만 할 때는 프로토타입에 값이 있는지 확인한 후 참조만 하지만

    인스턴스에서 프로토타입 속성으로 건드리는 경우, 즉 연산을 하려고 할 경우 인스턴스에 메모리가 주어지고,

    프로토타입의 속성을 호출하여 받은 값을 연산하여 할당된 메모리에 저장이 되어

    이렇게 할당받은 메모리로 인스턴스가 고유의 속성을 가지게 되는 것이다.

     

    간단히 말해 인스턴스에 메모리가 프로토타입으로부터 주어지는 경우는

    인스턴스에서 프로토타입의 속성을 변경하려고 할 때라고 말할 수도 있다.

     

     

    주의할 점은 인스턴스 자체에서도 프로토타입에 직접 접근하여 기본값을 바꿀 수도 있다.

     

    function TestFunc() {}
    
    TestFunc.prototype.pVal = 100; // 프로토타입에 속성을 부여하고 기본값으로 100 부여
    let testInstance = new TestFunc(); // 인스턴스 생성
    console.log(testInstance.pVal) // 100이 출력됨
    
    testInstance.pVal = 10; // 인스턴스의 pVal의 값을 10으로 변경
    console.log(testInstance.pVal) // 10이 출력되는 것 확인
    testInstance.__proto__.pVal = 50; // 원형 값을 50으로 변경
    
    let testInstance2 = new TestFunc(); // 새로운 인스턴스 생성
    console.log(testInstance.pVal) // 첫번째 인스턴스의 pVal은 여전히 10이 출력
    console.log(testInstance2.pVal) // 두 번째 인스턴스에서 접근한 원형값은 50이 출력

     

    위 코드에서 testInstance1이 원형값을 변경하자 testInstance2의 기본값에 반영된 것을 볼 수 있다.

    자바와 같은 언어에서는 class에서 객체를 생성할 경우, 객체에서 class를 건드릴 수 없다.

    그러나 자바스크립트에서는 생성자인 함수 뿐 아니라 객체까지 원형에 손을 댈 수 있다는 데 주의해야 한다.

     

    잠깐 그런데! 위 코드에서 __proto__ 라는 속성이 보인다.

    이 __proto__는 Prototype Link이다. 아래에서 더 자세히 보자.

     

     

     

    __proto__ 속성과 프로토타입 객체

    prototype 속성은 함수만 가지고 있던 것과는 달리,

    __proto__ 속성은 모든 객체가 빠짐없이 가지고 있는 속성이다.

     

    __proto__는 객체가 생성될 때 조상이었던 함수의 프로토타입 객체를 가리킨다.

    예를들어 위의 예시코드들에서 testInstance 객체는 TestFunc() 함수로부터 생성되었다.

    그렇다면 testInstance 객체는 TestFunc 함수의 프로토타입 객체를 가리키게 된다.

     

     

    ㅁ 프로토타입 체인

    또한 함께 알아두면 좋을 것은 프로토타입 체인이다.

    testInstance 객체가 어떤 속성을 직접 가지고 있지 않을 때,

    이 속성을 찾을 때까지 상위 프로토타입을 탐색한다.

     

    최상위 객체인 Object에까지 도달했는데도 해당 속성을 찾을 수 없을 때 비로소(?) undefined를 리턴한다.

    바로 이렇게 __proto__속성을 통해 상위 프로토타입과 연결되어 있는 형태를 프로토타입 체인이라고 한다.

     

    기본적으로 Object에 선언된 속성과 함수들,

    예를 들면 toString(), valueOf 등을 모든 객체가 사용할 수 있는 것도

    이러한 프로토타입 체인 구조 덕분이라고 할 수 있다.

     

     

    자바스크립트는 위와 같은 프로토타입 개념을 통해 '상속'과 같은 기능을 제공하고 있다.

    자바나 여타 언어에서 말하는 상속과는 다소 다른 개념이긴 하지만,

    자바스크립트에서도 중요하게 이용되고 있으므로 다음 글에서는 그 내용을 써보도록 하겠다.

     

     

     

     

     

    도움받은 글 :

    1) prototype : https://jsdev.kr/t/javascript-prototype/2853

    2) __proto__ : https://medium.com/@bluesh55/javascript-prototype-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-f8e67c286b67

    댓글

coding wanee