본문 바로가기
Frontend/JavaScript

JavaScript 프로토타입(prototype)과 생성자 함수

by 코딩쥐 2024. 7. 30.

JavaScript는 흔히 프로토타입 기반 언어라고 불린다. 프로토타입이란 객체 간의 상속을 구현하기 위해 사용된다. 부모 객체 역할을 하는 객체로부터 메서드와 속성들을 상속받기 위해 프로토타입 객체를 갖게 된다. 

더보기
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        let primitive = 2;
        let obj1 = {}
        console.dir(primitive);
        console.dir(obj1);
    </script>
</body>
</html>

 

기본형과 참조형을 선언하고 console.dir()을 사용하여 요소를 출력하면 위와 같이 참조형의 경우에만 프로토타입을 갖는 것을 알 수 있다. 기본형의 경우에는 상속받을 메서드와 속성이 없기 때문에 프로토타입을 가지고 있지 않는다. 모든 참조형의 경우 프로토타입을 갖고 있으며, 상속되는 속성과 메서드들이 객체의 생성자의 prototype에 정의되어 있다. 참조형의 프로토타입은 최종적으로 Object이다. 

 

프로토타입 프로퍼티(Prototype) 

프로토타입 프로퍼티란 Object객체 외에 함수 객체만 소유하고 있는 프로퍼티로, 프로토타입 프로퍼티의 기본 값은 함수 자신을 가리키는 constructor 프로퍼티이다. 

더보기
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        function Func1(){
            this.a = 1;
            this.b = 3;
        }
        console.dir(Func1);
    </script>
</body>
</html>

위에서 보다시피 함수의 경우 prototype이라는 프로퍼티를 가지고 있고, prototype 안에 constructor 프로퍼티는 Func1()이라는 자기자신을 가리키고 있는 것을 볼 수 있다. 그리고 prototype 프로퍼티 안에 [[prototype]] 속성도 있는데, 이는 참조형의 최종 프로토타입인 Object에 대한 링크이다. 이렇게 함수가 가지고 있는 프로토타입 프로퍼티를 사용해서 생성자함수(객체를 생성하는 역할을 하는 함수)를 만들 수 있다. 

 

프로토타입 링크([[Prototype]])

프로토타입 링크는 자신의 부모인 프로토타입 객체를 가리키는 참조 링크이다. Object의 새 객체를 만들어서 확인해봤을 때, Object의 부모 객체의 prototype 프로퍼티를 그대로 가져와 프로토타입 링크를 생성한 것을 볼 수 있다. 

더보기
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        console.dir(Object);

        // Object를 참조하는 객체 생성
        let objchild = new Object();
        console.dir(objchild);

    </script>
</body>
</html>

 

객체에서 프로퍼티를 탐색할 때 객체 내에 프로퍼티가 존재하지 않으면 [[Prototype]]을 참조하여 해당 내용이 존재하는지 탐색(프로토타입 체이닝)한다.  


생성자 함수

위의 설명에서 함수 객체는 프로토타입 프로퍼티를 소유하고 있다고 설명했다. 이 프로퍼티에 속성과 함수를 넣은 뒤에 new 연산자와 함께 호출하여 프로퍼티 구조가 동일한 객체 여러개 생성할 수 있게 된다. 생성자 함수를 사용하는 방법은 다음과 같다.

  1. 생성자 함수의 이름 첫글자는 대문자로 작성
  2. 생성자 함수는 파스칼케이스로 작성 (ex) CreateNewWorld )
  3. new 연산자를 붙여서 호출
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        // 생성자 함수 생성, 첫글자 대문자로 작성
        function Func1(name){
            this.name = name; 
            this.callname = function callname(){
                console.log(this.name);
            }
        }

        // 생성자 함수 호출 
        let childfunc1 = new Func1("코딩쥐");
        console.dir(childfunc1);
        childfunc1.callname();

    </script>
</body>
</html>

 

생성자 함수를 호출해서 Func1()의 prototype 프로퍼티를 복사해서 객체를 생성한 모습을 볼 수 있다. 

 

프로토타입 프로퍼티와 프로토타입 링크 접근방법

  • .property
    프로토타입 프로퍼티에에 접근하는 방법이다. 

  •  .__proto__
    프로토타입 링크에 접근을 할 수 있는 방법이나, 현재 MDN에서 확인해보면 성능상의 문제로 사용을 권장하지 않는다고 되어있다. 다음 메서드들을 사용하기를 권장하고 있다.
    • Object.getPrototypeOf(객체);
      객체의 [[Prototype]]을 반환하는 메서드 

    • Object.setPrototypeOf(객체, 프로토타입);
      객체의 [[Prototype]]의 프로토타입을 변경한다. 하지만 프로토타입을 변경하는 것 자체가 JavaScript의 엔진의 최적화를 방해하기 때문에 최대한 변경을 하지 않는 것을 권장한다. 
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        function Func1() {
            this.a = 10;
            this.b = 20;
        }

        // 새로운 객체 생성
        const obj1 = new Func1();

        // 부모 객체의 prototype 프로퍼티에 a1을 삽입하면, 자식 객체는 [[prototype]]링크를 통해 값을 참조한다.
        Func1.prototype.a1 = 2;
        console.log(obj1.a1); //2

        // 부모 객체의 prototype 프로퍼티 안의 [[prototype]]링크에 b1을 삽입하면, prototype 프로퍼티를 참조하기 때문에 자식 객체에서도 값을 불러올 수 있다.
        Func1.prototype.__proto__.b1 = 50;
        console.log(obj1.b1); // 50

        // 부모 객체의 [[prototype]]링크에 b2를 삽입해도, 자식 객체는 Prototype프로퍼티를 참조할 뿐 [[prototype]]을 참조하지 않기 때문에 값을 불러올 수 없다.
        Func1.__proto__.b2 = 100;
        console.log(obj1.b2); //undefined
        
        // 자식 객체의 prototype링크에 a2를 삽입하면, 같은 주소를 참조하고 있기 때문에 부모객체의 prototype 프로퍼티에 삽입된다.
        obj1.__proto__.a2 = "안녕하세요";
        console.log(Func1.prototype.a2); // 안녕하세요

        // 하지만 setPrototypeOf()를 사용하면 부모객체의 prototype 프로퍼티에는 영향이 생기지않고, 
        // 본인의 [[property]]링크가 새로 생기면서 그곳에 값이 저장된다.
        let a3 = { a3: "abcdef"};
        Object.setPrototypeOf(obj1, a3);
        console.log(obj1.a3); // abcdef
        console.log(Func1.prototype.a3); //undefined;
    </script>

</body>

</html>