prototype 두번째 정리 들어갑니다.
※ 객체의 관계에 대하여
자바스크립트의 모든 객체는 Object 객체를 그 원형으로 두고 있습니다. 근데 문제는 자바스크립트에 미리 정의된 객체가 Object 만 있는게 아니라는거죠. String, Array, Number 등의 Wrapper 객체가 여러개 있고, web이냐 nodeJS냐에 따라 미리 정의된 여러개의 객체가 다 다르죠. 우리가 만약 Number 객체의 원형을 이용하되, 그대로 호출하지 않고 몇가지 기능을 더 추가해서 StrongNumber 라는 객체 원형을 만들고 싶다면 어떻게 해야할까요? 혹은 기존 자바스크립트의 Date 객체 기능이 맘에 안들어서 내가 직접 커스터마이징을 하겠다면? 마치 자바의 상속 구조를 타는 거 같은 느낌을 자바스크립트에도 적용하면 좋지 않을까요? prototype을 이용해서 객체간의 의존관계를 만드는 방법을 prototype chaining 이라고 합니다.
※ prototype chaining
자바스크립트의 모든 객체는 Object 객체를 그 원형으로 두고 있다면, Array, Date 등의 자바스크립트 기본 객체들은 정의되어 있는 걸까요? 이들 객체의 원형도 분명 Object 객체 원형일텐데요. 우리는 이에 대한 해답을 prototype에서 찾을 수 있습니다.
Array 객체의 원형을 찍어봤는데요. __proto__ 로 객체의 프로트타입에 접근 가능하다고 했었죠? new 키워드로 생성한 Array 객체는 prototype이 Array 객체의 생성자 함수의 영역으로 되는거죠. 근데 new Array().__proto__.__proto__ 를 볼까요? Object 라고 나오네요. __proto__.__proto__를 확장해보죠.
__proto__.__proto__.constructor 가 Object 객체의 생성자 함수네요. 즉 Array 객체의 원형은 Object 객체의 원형을 프로토타입으로 보고 있는거죠. 이런 방식이 바로 prototype chaining입니다. 새로운 객체 원형의 프로토타입을 기존에 어떤 객체와 연결을 시켜서 프로토타입의 프로토타입으로 접근가능하게 하는거죠.
※ prototype chaining의 구현
이 방식을 이해하는 제일 좋은 방법은 예제 소스를 만들어보는거겠죠? 간단한 예제를 만들어보았습니다.
function Person(_name, _age, _sex, _contact){ this.name = _name || '이름모름'; this.age = _age || '나이모름'; this.sex = _sex || '성별모름'; this.contact = _contact || '연락처없음'; } Person.prototype.getInfo = function(){ return { name : this.name, age : this.age, sex : this.sex }; } function Employee(_name, _age, _sex, _contact, _dept, _grade, _salary){ this.name = _name || '이름모름'; this.age = _age || '나이모름'; this.sex = _sex || '성별모름'; this.contact = _contact || '연락처없음'; this.dept = _dept || '미발령'; this.grade = _grade || '미분류'; this.salary = _salary || 0; } Employee.prototype = new Person(); Employee.prototype.constructor = Employee; Employee.prototype.getEmpInfo = function(){ let obj = this.getInfo(); obj.dept = this.dept; obj.grade = this.grade; obj.salary = this.salary; return obj; }
실행결과는 다음과 같습니다.
Employee 객체가 생성되었는데, 상위객체인 Person의 프로퍼티를 중복으로 갖고 있네요. 정확히 말하면 Person의 프로퍼티에 접근을 못하고 있는거죠. 덕분에 입력이 제대로 안된거처럼 보일수 있어서 상당히 문제가 될거 같죠?
그럼 Person의 프로퍼티로 접근가능하도록 바꿔야 할텐데요. 이걸 해결하려면 Employee와 Person사이에 Bridge 역할을 하는 객체가 필요해서 서로 상-하위 관계를 설정해주어야 합니다. 이걸 이용해서 위의 예제를 다음과 같이 수정했습니다.
const extendClass = (function(){ function Bridge(){} return function(Parent, Child){ Bridge.prototype = Parent.prototype; Child.prototype = new Bridge(); Child.prototype.constructor = Child; Child.prototype.superClass = Parent; } })(); function Person(_name, _age, _sex, _contact){ this.name = _name || '이름모름'; this.age = _age || '나이모름'; this.sex = _sex || '성별모름'; this.contact = _contact || '연락처없음'; } Person.prototype.getInfo = function(){ return { name : this.name, age : this.age, sex : this.sex }; } function Employee(_name, _age, _sex, _contact, _dept, _grade, _salary){ this.superClass(_name, _age, _sex, _contact); this.dept = _dept || '미발령'; this.grade = _grade || '미분류'; this.salary = _salary || 0; } extendClass(Person, Employee); Employee.prototype.getEmpInfo = function(){ let obj = this.getInfo(); obj.dept = this.dept; obj.grade = this.grade; obj.salary = this.salary; return obj; }
Bridge 객체는 객체간의 상-하위 설정 외에는 외부에서 쓰일 일이 없기 때문에 굳이 노출시킬 필요가 없겠죠? 그래서 클로저로 extendClass의 실행부분을 감싸서 Bridge 객체를 접근하지 못하게 하며, 우리가 원하는 상-하위 객체설정만 하는 함수를 반환하게 했습니다.
➣ 클로저는 1회성 코드라고 생각하시면 편한데요. 정확하게는 자바스크립트 소스가 쭉 실행될때 한번 딱 돌고 끝나는 코드 부분입니다. 클로저 부분에서 return으로 함수를 넘기는걸 extendClass 변수가 받아서 extendClass 를 함수로 만드는거죠.
실행결과는 다음과 같습니다.
Employee를 구현한 객체에서 프로퍼티의 중복이 사라려서 훨씬 깔끔해졌네요. Person과 Employee의 생성자를 extendClass 함수로 상-하관계를 설정했기 때문에 Employee의 생성자를 실행하면서 만들어진 객체는 하나의 실행컨텍스트가 되어서 프로퍼티가 모두 한곳에 모인거 같습니다.
원리를 이해하기 위해 prototype chaining을 직접 구현했지만 계속 이런식으로 코드를 짠다고 생각하면... 솔직히 가독성도 떨어지고 별로 좋을거 같지는 않네요. 사실 초보자 입장에선 이런 부분들이 난이도를 높이는데 한몫 하는거고요. 그래서 자바스크립트에서는 간단하게 prototype chaining을 설정할 수 있는 방법을 제공하고 있습니다. 이건 다음 포스트에 이어서 작성하겠습니다.
※ 레퍼런스
이 글은 인프런 사이트의 '핵심개념을 알아보는 Javascript Flow' 강좌의 내용을 토대로 예제코드를 작성하여 설명하였습니다. 클로저 및 prototype 개념을 포함한 기본 자바스크립트를 더 깊이 이해하기를 원하시면 해당 강좌를 수강하시는 것을 권장합니다. 이 강좌는 무료입니다.
'Web 개발 (업데이트 없음)' 카테고리의 다른 글
ES6 문법 정리 개요 (0) | 2018.10.28 |
---|---|
자바스크립트 개념 정리 (3) – prototype 3탄 (자바스크립트 내장 메소드, 상속구조 구현을 위한 ES6 문법) (0) | 2018.10.28 |
자바스크립트 개념 정리 (3) - prototype 1탄 (0) | 2018.10.28 |
자바스크립트 개념 정리 (2) - context & scope (0) | 2018.10.28 |
자바스크립트 개념 정리 (1) - 변수 타입 (0) | 2018.10.28 |