-
저번에 일반 함수로 생성자 함수처럼 구현 하면서 생성자 함수를 굳이(필수적으로) 사용해야 하는가에 대한 궁금증이 남았었다.
완벽한 해답을 얻은 건 아니지만 그래도 궁금증이 조금은 해소될만한 답이 나와서 글을 써본다.
저번 포스팅에서 다음과 같이 언급했다.
실제로 두 함수의 차이는 생성자가 객체인지 사용자 정의 생성자인지의 차이와 프로토타입의 구성 뿐이다.
게다가 상속받은 프로토타입의 메서드도 완벽히 동일하다....거의 맞는 말이긴 한데(둘째 줄은 틀린 거였음.. 죄송합니다 ㅜ), 프로토타입의 구성에서 js 내부적으로 꽤나 유의미한 차이가 나는거 같다.
그 전에 프로토타입의 정의부터 살펴보자.
js에서의 프로토타입은 부모 객체로 부터 상속받은 프로퍼티(속성)들의 집합(객체)을 의미한다.
java에 extends와 python의 () 키워드와 같은 역할을 하게된다.
다음 코드는 생성자 역할을 하는 함수에 프로토타입 객체의 속성을 추가하는 코드이다.
//생성자 함수로 프로토타입 구현 const Bmw = function (color) { this.color = color; }; Bmw.prototype.wheels = 4; Bmw.prototype.drive = function () { console.log("Drive..."); }; Bmw.prototype.nevigation = 1; Bmw.prototype.stop = function () { console.log("Stop"); }; //일반 함수로 프로토타입 구현 const audi = function (color) { return { color: color, }; }; // audi.prototype.wheels = 4; // audi.prototype.drive = function () { // console.log("Drive..."); // }; // audi.prototype.nevigation = 1; // audi.prototype.stop = function () { // console.log("Stop"); // }; const x5 = new Bmw("Red"); const z4 = audi("Blue"); z4.__proto__ = { wheels: 4, drive: function () { console.log("Drive..."); }, nevigation: 1, stop: function () { console.log("Stop"); }, };
일단 코드 구성에서부터 차이가 있다.
생성자 함수는 생성자 함수 인스턴스 객체의 __proto__객체에 들어가야할 속성을 .prototype 프로퍼티를 통해 넣을 수 있도록 지원하고 있다.
반면 일반 함수는 지원하지 않기 때문에 객체를 생성한 후에 직접 __proto__객체에 속성을 넣어줘야 한다.
또한 콘솔창 결과에서도 차이가 있다.
바로 constructor 속성의 유무이다.
constructor은 new 연산자에 의해 만들어지기 때문에 생성자 함수 인스턴스 객체에만 존재하는 것을 확인할 수 있다.
참고로 생성자 함수에만 .prototype 프로퍼티를 지원하는 이유도 new 연산자 때문인거 같다. prototype과 constructor는 한 쌍을 이루니까.
이 코드를 보면 위에 내용이 보다 명확해 질거 같다.
console.log(x5 instanceof Bmw); console.log(z4 instanceof audi); console.log(x5.constructor === Bmw); console.log(z4.constructor === audi);
따라서 (내 생각엔) 일반 함수로 객체를 만들지 않고 생성자 함수로 객체(인스턴스)를 만드는 이유는 다음과 같다.
상속의 편의성과 상속 관계를 명확하게 정의하기 위해!
그런데, 상속의 편의성 이라기엔 모든 속성에 대해 .prototype을 붙여주는건 맞지 않는거 같다.
간단하게 상속할 수 있는 방법은 없을까?
크게 두 가지 방법이 있다.
첫 번째 방법은 .prototype에 객체를 넘겨주는 것이다.
//Before Bmw.prototype.wheels = 4; Bmw.prototype.drive = function () { console.log("Drive..."); }; Bmw.prototype.nevigation = 1; Bmw.prototype.stop = function () { console.log("Stop"); }; //After Bmw.prototype = { wheels: 4, drive: function () { console.log("Drive..."); }, nevigation: 1, stop: function () { console.log("Stop"); }, };
그런데 이러면 prototype에서 constructor부분을 날리게 되어 에러가 발생할 수 있다.
이를 해결한 최종 코드는 다음과 같다.
//Final Bmw.prototype = { constructor: Bmw, wheels: 4, drive: function () { console.log("Drive..."); }, nevigation: 1, stop: function () { console.log("Stop"); }, };
constructor을 수동으로 명시해 주는 것이다.
두 번째 방법은 class를 사용하여 extends 연산자를 사용하는 것이다.
class Car { constructor(color, blind) { this.color = color; this.blind = blind; this.wheel = 4; } drive() { console.log("drive..."); } stop() { console.log("stop..."); } } class Bmw extends Car { constructor(sheet, ...args) { super(...args); this.sheet = sheet; } park() { console.log("parking..."); } stop() { super.stop(); console.log("OFF"); } } const z4 = new Bmw(4, "Blue", true); z4.drive(); z4.park(); z4.stop();
Class에 대한 포스팅은 다음에 작성하는걸로...ㅎ
요약
상속을 하면 prototype 객체가 만들어진다.
상속 받은 prototype를 편하고, 안전하게 관리하기 위해
일반 함수 말고 생성자 함수나 Class를 이용하는 것이다.
(역시 굳이 안 쓰는데는 이유가 있다.)'언어 공부 > JS' 카테고리의 다른 글
일반 함수로 생성자 함수처럼 구현 (0) 2022.04.07 Closure (0) 2022.04.07 console.log() 실시간 적용 (0) 2022.04.06