ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Object.create()와 constructor
    개발 2020. 5. 11. 23:35

     

    mdn에서의 Object.create()의 정의

     

      Object.create() 메서드는 지정된 프로토타입 객체 및 속성(property)을 갖는 새 객체를 만듭니다.

     

     

    먼저 Object.create()는 새 객체를 만들어 준다고 정의되어 있습니다.

     

    그렇다면 이  Object.create() 메서드가 어떻게 새 객체를 만들어주는지와 constructor의 사용 법에 대해서 자세히 알아보겠습니다.

     

     

    Object.create()로 새 객체 만들어 보기

     

    // Human 상위 class 생성
    function Human(name) {
        this.name = name
    }
    
    //Human.prototype에 method 추가
    Human.prototype.sleep = function() {console.log(`${this.name} Human`)}
    
    //test
    let steve = new Human('steve')
    steve.name // 'steve'
    steve.sleep() // 'steve Human'
    
    //Student 하위 class생성
    function Student(name) {
        this.name = name
    }
    
    //Human.prototype을 Student.prototype으로 상속 연결
    Student.prototype = Object.create(Human.prototype)
    
    //Student.prototypedp method 추가
    Student.prototype.learn = function() {console.log(`${this.name} is learning`)
    
    //test
    let john = new Student('john')
    john.name // 'john'
    john.sleep() // 'john Human'
    john.learn() // 'john is learning'

     

    Human이라는 상위 클래스가 있고 Human의 프로토타입에 sleep이라는 메서드를 추가하였습니다.

    steve라는 변수에 new Human('steve');로 객체를 생성하였고, steve.name과 steve.sleep() 메서드 모두 의도한대로 잘 출력하고 있습니다.

     

    그 다음 Student라는 하위 클래스를 만들고 Student의 프로토타입에 Object.create(Human.prototype)으로 Human.prototype으로 상속연결하여 새 객체를 할당하였습니다.

    그리고 Student의 프로토타입에 learn이라는 메서드를 정의하였고 john이라는 변수에 new연산자를 통해 생성자 객체를 만들었습니다.

    john.name과 john.sleep(), john.learn() 메서드가 this.name을 모두 john을 받아와서 출력하고 있는데 여기서 의문점이 생깁니다.

     

     

    john에는 sleep메서드를 정의한 적이 없는데 어떻게 출력되고 있는 것일까요?

     

    먼저 위의 Human과 Student를 console.dir() 로 출력해서 살펴보겠습니다.

     

     

     

     

    출력결과 Student의 constructor가 Student 자신이 아닌 Human을 가리키고 있습니다.

    Human에 속한 프로토타입을 보면 sleep 메서드와 constructor: ƒ Human(name)을 확인할 수 있고 

    Student의 프로토타입에는 learn 메서드는 존재하고 있으나 constructor가 존재하지 않고 있음을 알 수 있습니다.

     

    물론 prototype과 연결되어 있는 __proto__를 열어보면  Human에서와 같은 constructor: ƒ Human(name) 을 찾을 수는 있습니다.

     

     

     

     

    Student 자기 자신의 생성자를 가지고 있지 않은 이유는 Object.create()을 사용할 경우, 새로 만들어진 객체에는 constructor 가 존재하지 않습니다. 따라서 constructor를 만들고, 그 프로퍼티가 자신을 생성한 함수를 가리키도록 재 할당하는 작업을 거쳐야 합니다.

     

     

    이 과정을 코드로 옮겨보면 다음과 같이 작성해주어야 합니다.

    Student.prototype = Object.create(Human.prototype);
    
    Student.prototype.constructor = Student;

     

    위의 코드와 연결해서 보면 Object.create(Human.prototype);으로 새 객체를 만들어 준 후에, Student.prototype.constructor = Student; 로 프로토타입 생성자에 자기 자신을 할당해 주어야 합니다.

     

    할당해 준 결과를 콘솔로 출력하면 다음과 같이 생성자가 생긴 것을 확인할 수 있습니다.

     

     

     

    이렇게 constructor를 사용하지 않으면 Student와의 연결고리가 끊어지기 때문에 constructor에 자기 자신을 명시적으로 할당해주어야만 연결고리가 이어집니다.

     

     

     

    정리하기

     

    위에 코드를 보면 constructor를 선언해 주지 않더라도 john.sleep() 메서드가 잘 작동하는 것을 확인했습니다.

     

    Q ! 그렇다면 constructor를 선언해주지 않더라도 new 키워드로 새로운 인스턴스가 생성되고 해당 기능들도 잘 구현이 되는데 constructor에 명시적으로 꼭 자기 자신을 연결시켜주어야 하는 이유는 무엇일까요?

     

    A ! new 키워드로 새로운 인스턴스는 생성이 됩니다. 

    그러나 new Object()를 사용할 때 문제점이 있기 때문에 생겨난 방식 중 한 방식이라고 볼 수 있습니다.

    조금 복잡하고 지저분한 방식이라고 생각할 수 있겠지만 자바스크립트는 처음에 OOP를 고려하지 않고 만든 언어이기 때문에 상속의 구조를 정확하게 만들기 위해서 필요한 방법이라고 할 수 있습니다. 이 문제를 쉽게 구현해보고자 나온 키워드가 ES6의 Class 키워드 입니다.

    즉, 한줄로 요약하자면 구조의 정확성을 위해 사용한다고 보면 됩니다.

     

     

    Q ! 구조의 정확성이 떨어지더라도 기능상의 변화가 없는데 구조가 정확하지 않으면 생기는 문제점이 무엇인가요?

     

    A ! 우리가 클래스를 정의할 때 앞 글자를 대문자로 정의하고, 상수를 만들 때 컨벤션을 지키지 않아도 작동은 되는데 굳이 지키는 이유를 생각하면 됩니다. constructor가 내가 new 키워드를 통해 만든 클래스와 이름이 다르다면, 해당 클래스를 쓰는 사람은 내가 제대로 작성한 코드인지 확신이 들 수 없을 것이고 결국엔 코드를 작성한 본인도 혼란스러운 상황을 겪게 될 것입니다.

     

     

     


     

     

    Object.create() 가 너무 복잡해서 사용하고 싶지 않아요.

    그냥 prototype으로 직접 연결하면 안되나요?

     

     

    prototype으로 바로 연결하는 방법을 사용하면 어떤 문제점이 나타나는지 알아보겠습니다.

     

    Student.prototype = Human.prototype;
    
    console.log(Student.prototype);

     

    이 전 코드는 동일하고 Student.prototype = Humen.prototype으로 프로토타입을 직접 할당해보았습니다.

     

     

     

     

     

    결과를 보면 Object.create() 없이 바로 할당하여도 Student의 프로토타입 객체에 Humen 프로토타입 객체가 보입니다.
    즉, Student 프로토타입 객체의 sleep 메서드가 잘 보이는 것을 알 수 있습니다.

     

    그렇다면 Object.create()를 한 것과 무슨 차이가 있는지 알아보겠습니다.

    console.log(Student.prototype.constructor);
    
    Student.prototype.constructor = Student;
    
    console.log(Student.prototype.constructor);
    
    console.log(Human.prototype.constructor);

     

     

     

    콘솔 출력 결과를 차례대로 보면 아직 constructor를 명시적으로 할당해주기 전의 콘솔 출력 값은 Student.prototype.constructor가 Human을 출력하고 있습니다. 

    즉, Student 의 생성자 권한을 Human이 가지고 있다는 것입니다.

     

    이 문제를 해결하기 위해서 Student.prototype.constructor = Student; 로 Student의 생성자에 자기 자신을 명시적으로 할당해준 결과 의도 한대로 Student를 잘 출력하는 것을 확인했습니다.

     

    하지만 여기서 문제는 Human의 생성자도 Student를 출력해내고 있다는 것입니다. 위에서 Student.prototype에 Human.prototype을 바로 할당해주었기 때문에 Student의 생성자가 명시적으로 부여되면서 같은 주소 값을 바라보고 있는 Human.prototype의 생성자도 바뀌게 되는 것입니다.

     

     

    따라서 위의 내용을 정리요약 하자면 다음과 같습니다.

     

    • Object.create()를 사용하지 않고 prototype으로 할당하게 된다면 Student과 Human은 같은 주소 값을 바라보고 있는 것입니다.

    • 이 문제를 해결해보고자 Student의 생성자에 자기 자신을 명시적으로 할당 해주면 될 것이라고 생각 했지만 같은 주소 값을 바라보고 있는 Human의 생성자도 Student를 바라보게 됩니다.

     

     

    이러한 문제가 있기 때문에 Object.create()를 활용해야 하고 Object.create()를 사용하고 나서 constructor에 생성자를 명시적으로 부여해 주는 것이 우리가 의도한 대로 각각의 생성자를 잘 갖은 채로 객체가 생성됩니다.

     

    '개발' 카테고리의 다른 글

    JavaScript 비동기 처리와 콜백 함수  (0) 2020.05.19
    HTTP 통신  (0) 2020.05.19
    ES6 - class로 작성한 OOP  (0) 2020.05.08
    화살표 함수( Arrow Function )에서의 this 바인딩  (0) 2020.04.29
    ES6 문법 - Arrow Function  (0) 2020.04.28
Designed by Tistory.