ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [JS] 자바스크립트, 생성자함수(Constructor function)와 프로토타입 체인(Prototype chain)
    Frontend 2019. 5. 31. 19:57

    오늘은 지난번 수업 시간에 배운 생성자 함수와 프로토타입 체인에 대해서 정리해보려고 한다.

     

     

    생성자 함수 (The Constructor Function)

    생성자 함수란 쉽게 말해서 new 키워드와 함께 쓰이는 함수이다. 우리가 직접 함수를 정의하여 new 키워드로 생성자 함수를 만들어 사용할 수도 있지만, 자바스크립트에 기본적으로 내장된 생성자 함수를 사용할 수도 있다.

     

    new Array();

    new Object();

    new Function();

     

    바로 이 3가지이다.

     

    생성자 함수는 특이하게 함수의 이름이 모두 대문자로 시작한다. 그래서 우리가 생성자 함수를 직접 정의해서 쓸 때에도 관례적으로 함수 이름의 첫 글자를 대문자로 쓴다.

     

     

    var arr = [];
    var arr = new Array();

     

    이 두 코드는 모두 같은 동작을 한다. 그러니까 두 코드 모두 빈 배열을 만든다. 그러나 아래의 코드는 자바스크립트의 기본 내장된 생성자 함수를 이용하여 배열을 만든 것이다.

     

     

     

    생성자 함수는 말 그대로 함수이다.

    그리고 자바스크립트에서 모든 함수는 객체이다.

    그러므로 생성자 함수 또한 객체이다.

     

    자바스크립트에서 객체란 속성(key)값(value)를 가지는 존재이다.

    즉, 생성자 함수 또한 객체이므로 속성과 값을 가진다. 

     

    모든 생성자 함수에는 기본적으로 prototype이란 속성이 자동으로 생기는 데,

    우리가 콘솔창에 Object.prototype이라고 타이핑하면 아래와 같이 확인 할 수 있다.

     

    그런 데 자세히보면 재밌는 사실을 발견할 수 있다.

     

     

     

    Object.prototype의 값으로 리턴된 객체에는 

    다시 constructor라는 속성이 들어있다.

     

     

     

     

    그러니까 constructorprototype은 한 쌍을 이루며

    서로가 서로의 정보를 가지고 있다.

     

    예를 들어 constructor가 남편이라면 prototype은 남편의 아내와 같은 관계이다.

    그리고 남편과 남편의 아내는 서로의 정보를 가지고 있다.

     

    남편은 constructor이고

    남편의 아내는 prototype이다.

    남편의 아내의 남편은 constructor이고,

    남편의 아내의 남편의 아내는 prototype이다.

     

     

     


    생성자 함수의 인스턴스 (Instance)

    var obj = new Object();

     

    모든 생성자 함수는 this라는 빈 객체를 리턴한다.

    그러므로 위 코드에서 obj 변수에는 빈 객체가 할당된다.

    이 때 생성자 함수에서 리턴하는 결과물을 우리는 인스턴스(instance)라고 한다.

     

    인스턴스는 constructor와 prototype 사이에서 생성된 자식과도 같은 존재이다.

     

     

     


    프로토타입 체인 (Prototype Chain)

    인스턴스(Instance)는 부모의 prototype에 접근할 수 있다는 특징이 있다.

     

     

    var obj = new Object();
    
    console.log(obj.constuctor);

     

    만약 우리가 위와 같이 obj라는 인스턴스를 생성하고

    obj.constructor에 접근을 한다고 생각해보자.

     

     

    그러면 이렇게 constructor의 값을 가져올 수 있는 데,

    그 이유는 obj의 엄마인 Object.prototype이 constructor의 정보를 가지고 있고,

    obj는 엄마의 정보에 접근하여 값을 가져올 수 있기 때문이다.

     

     

    우리가 Object.prototype에 haha라는 속성과 123이란 값을 추가하면

    우리는 obj.haha를 하는 것만으로 숫자 123을 가져올 수 있다.

     

     

    function Foo () {}
    
    var k = new Foo();
    var j = new Foo();

     

    다른 비슷한 코드를 다시 살펴보자.

     

    이 코드에서 Foo라는 constructor가 생성되었고,

    Foo는 prototype을 속성으로 가진다.

    그리고 우리는 k와 j라는 2개의 인스턴스가 생성된 것을 확인할 수 있다.

     

    그러니까 위 코드에서 k와 j는 Foo.prototype을 통해

    constructor 정보에 접근할 수 있다.

     

     

    Foo.prototype.constructork.constructor가 동일한 값을 가져오는 것을 확인할 수 있다.

     

     

    function Foo () {}
    
    var j = new Foo();
    
    Foo.prototype.name = "JY";
    console.log(j.name);
    
    j.name = "Haha";
    console.log(j.name);

     

    그렇다면 위와 같은 상황에서 j는 어떤 name값을 가져올 것인가?

     

     

     

    결과는 이렇다.

    우리가 j에 name이란 속성과 "HaHa"라는 값을 부여하기 전에는

    상위의 Foo.prototype으로 올라가서 name값을 찾아 가져온다.

    그러나 j에 name이란 속성을 부여하고 난 후에는 j에 부여된 속성값을 가져온다.

    그리고 이런 특성을 Prototype Chain이라고 한다.

     

     

     

     

     

    보통 우리는 일반적으로 Prototype Chain을 설명하면서 '상속'이라는 단어를 사용한다. 인스턴스가 부모 프로토타입에 접근하여 정보를 가져다 쓸 수 있다는 특성을 부모의 정보가 상속된다고 표현한다. 그러나 엄밀히 말하면 프로토타입 체인은 상속이 아니라 위임에 가깝다. 그러니까 부모 프로토타입의 모든 정보가 자식 인스턴스에게 이전되는 것이 아니기 때문이다. 그저 인스턴스는 필요할 때에만 상위 프로토타입에 접근하여 잠시 부모 프로토타입의 정보를 빼오거나 메소드를 위임받아 쓸 수 있는 것 뿐이다.

     

    var arr = new Array();
    
    arr.push(3);

     

    우리가 만약에 arr이라는 인스턴스를 만들었다고 생각해보자.

    arr에 3이란 숫자를 넣을 때 push 메소드를 사용할 수 있는 이유는

    바로 arr의 상위 프로토타입인 Array.prototype이 push 메소드를 가지고 있기 때문이다.

     

    그래서 arr은 필요할 때에 상위 프로토타입에 접근하여

    push라는 메소드를 가져와 연산을 수행할 수 있는 것이다.

     

     

     

     

     


    Dunder Proto ( __proto__ )

    Dunder Proto는 __proto__라고 표기하며, 상위 프로토타입을 가져올 때 사용한다.

     

    var ken = new Person('ken huh');
    console.log(ken.constructor === Person);  // true
    console.log(ken.__proto__ === Person.prototype);  // true

    예를 들어 위 코드를 보면, ken.constructorPerson 자체와 동일하다.

    아까 설명했듯이 ken이란 인스턴스는 부모 프로토타입에 접근하여 constructor 정보를 가져올 수 있다.

    그리고 ken.__proto__ 라고 적으면 이는 Person.prototype과 동일하다.

    즉, 바로 상위의 프로토타입을 가져온다.

     

     

    var o = new Object();
    console.log(o.constructor === Object); // true
    
    o.constructor = function bar () {}
    console.log(o.constructor === Object);  // false

     

    이 코드를 살펴보면 o.constructor는 Object를 가리킨다.

    그러나 o.constructor에 bar () 함수를 할당하면

    이제 o.constructor는 bar () 함수의 주소값을 가진다.

    그러므로 Object와 동일하지 않게 된다.

     

     

    그렇다면 만약에 Object.prototype의 상위 prototype을 가져오면 뭐가 출력이 될까?

     

     

    정답은 바로 null이다. 프로토타입 체인의 가장 최상단에 존재하는 Object의 prototype은 null이다.

     

     

    function Foo () { }
    
    var f = new Foo();
    console.log(f.__proto__ === Foo.prototype);
    
    Object.prototype.haha = 123;
    console.log(f.haha);

     

    위 코드를 살펴보자. Foo ()라는 함수를 생성자 함수로 만들어 f라는 인스턴스를 만들었다.

    이제 f.__proto__는 Foo.prototype과 동일하다는 것을 안다.

     

    만약에 Object.prototype.haha = 123;을 한 후 f.haha를 출력하면 프로토타입 체인에 의해 123이 출력된다.

     

    f 인스턴스는 먼저 자기 자신에게 haha라는 속성이 있는지를 찾고 없으면

    상위 프로토타입인 Foo.prototype으로 가서 haha라는 속성을 찾는다.

    그러나 Foo.prototype에도 해당 속성이 없으므로 더 상위의 프로토타입인

    Object.prototype에 접근하고 haha라는 속성을 찾아 123이란 값을 출력하는 것이다.

     

     

    반응형

    COMMENT