[프로토타입 기반 객체지향]
- 자바스크립트는 클래스가 존재하지않음
- 객체지향 프로그래밍을 할 수 있음 : 객체, 인스턴스 변수/메서드, 정보은닉(클로저), 상속(prototype, __proto__)
- 자바스크립트 객체지향 핵심 : 프로토타입 객체(체인)
- 해당 포스팅을 통해 단계별로 상속, 정보은닉 하는 방법을 알아볼 것 : 자바스크립트는 어떻게 구현하나
[자바스크립트로 객체지향 프로그래밍하기]
1) 기본적인 인스턴스 생성 구조
1 2 3 4 5 | function Person(name){ this.name = name; } var jinbro = new Person("jinbro"); | cs |
- Person 함수 : 클래스 역할이자 생성자(객체 생성 시 처음 호출 : 인스턴스 변수 값 및 메서드 설정을 위해) 역할
- new 키워드 : 생성자 호출 및 객체 생성(프로토타입 체인)
2) 문제가 많은 인스턴스 생성 구조
1 2 3 4 5 6 7 8 9 10 11 12 | function Person(name){ this.name = name; this.setName = function(name){ this.name = name; } this.getName = function(){ return this.name; } | cs |
- 1번과 동일구조 : 달라진 것은 객체마다 가지는 인스턴스 변수 값 은닉을 위해 get, set 메서드로만 정보 참조(완벽한 은닉 아님)
- 치명적인 것은 setName과 getName 메서드 this로 설정, 객체.메서드 일 때 this 바인딩 : 객체마다 동일한 메서드를 각각 가진다는 것
- 각 객체마다 동일한 기능을 하는 메서드를 각각 가진다는 것 메모리 낭비 : 인스턴스가 많아지고, 메서드가 무거워질 경우 ㄷㄷ
3) 프로토타입 객체를 통한 인스턴스 생성 구조
1 2 3 4 5 6 7 8 9 10 11 | function Person(name){ var name; } Person.prototype.setName = function(name){ name = name; } Person.prototype.getName = function(){ return name; } | cs |
- 자바스크립트 객체지향 : 공통적인 역할을 하는 것은 프로토타입 객체를 통해, 각각의 역할을 하는 것은 this로 해결
- 프로토타입 체인 : Person의 prototype 속성, Person 생성자를 통해 생성된 객체가 가진 __proto__ 연결 리스트 형태
- Person 생성자로 생성된 객체는 프로토타입체인을 통해 Person.prototype 객체 내 두 메서드를 사용함
- this는 가까운 객체와 바인딩 : 사람객체.메서드, 각 객체에 맞게 name은 설정되고 가져옴
- 정보은닉 : 각 객체를 통해서만 각 name(인스턴스 프로퍼티)에 접근, 수정 가능함
4) 프로토타입 객체 + 동적할당(메서드 추가) 방법
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | Function.prototype.method = function(name, func){ if(!this.prototype[name]) this.prototype[name] = func; } function Person(name){ this.name = name; } Person.method('setName', function(name){ this.name = name; }); Person.method('getName', function(){ return this.name; }); | cs |
- 자바스크립트 함수는 일급이기때문에 값으로 사용할 수 있음
- Person 생성자에 동적할당 시 메서드가 현재 존재하는지 하지않는지 기능을 넣지않고 Function에 넣는 이유
1) Person 상위 개념 함수는 Function : Function의 객체는 Person 그리고 모든 함수 - 프로토타입 체인
2) Person 에서만 사용할 기능이 아니기때문에 : Person만 존재하는 프로그램이면 모르겠지만...
3) 상위 함수는 되도록이면 건드리지 않는 것이 바람직함 : 팀원끼리 프로그래밍한다면, 공식 기능이 아니라서...
4) 동일한 메서드를 걸러내는 기능(공통), 각 추가되는 메서드는 각 객체 함수(개별)
- Person 생성자를 통해 생성된 객체는 prototype 객체를 복사하는 것이 아니라 연결하여 참조하는 것
- 그렇기때문에 Person.prototype 객체 내용이 추가되더라도 객체가 참조할 수 있는 메서드 리스트가 늘어난 것
5) 프로토타입 이용하지않고 함수 패턴으로 상속 + 인스턴스 생성 구조
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | var mammal = function(spec){ var obj = {}; obj.name = spec.name; obj.getName = function(){ return spec.name; }; return obj; } var cat = function(spec){ var that = mammal(spec); //super 역할 that.butler = spec.butler; /* that 객체의 getName 오버라이딩 역할 */ that.getName = function(){ return that.butler +"의 고양이 : " + that.name }; return that; } var myCat = cat({ name: "marry", butler: "jinbro" }); var yourCat = cat({ name: "nabi", butler: "jinhyung" }); | cs |
- 프로토타입 프로퍼티 활용하지않고 상속, 인스턴스 구조 생성
- 생성된 객체는 window(전역객체)의 프로퍼티에 추가
- mammal 함수가 생성자 역할 mammal에서 리턴되는 객체를 가지고 객체화
- 결국 리터럴로 객체를 생성하는 방법을 체계화시킨 것
6) 상속 + 오버라이딩 구현 + 인스턴스 생성
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | function Animal(name){ this.name; } Animal.prototype.getName = function(){ return this.name; } function Cat(name, butler){ this.name = name; this.butler = butler; } Cat.prototype = new Animal(); /* 오버라이딩 */ Cat.prototype.getName = function(){ return this.name + ", " + this.butler; } var myCat = new Cat("marry", "jinbro"); myCat.getName(); // "marry, jinbro" Animal.prototype.getName = function(){ return "Hello!, " + this.name; } var yourCat = new Cat("nabi", "jinhyung"); yourCat.getName(); // "nabi, jinhyung" | cs |
- Animal 함수의 프로토타입 객체를 손상시키지 않으면서, 인터페이스를 구현하는 방식(Animal의 getName을 Cat에 맞게 구현)
- Cat.prototype = Animal 생성자의 객체 프로토타입(__proto__) 프로퍼티 참조
- 생성될 때만 참조 : 생성 당시의 Animal 프로토타입 프로퍼티가 중요, 이후부터는 별개 - 오버라이딩 시점
- 프로토타입 특성으로 오버라이딩하기
- 코드 재사용 : 개발 비용, 시간 절약
- 관계를 파악하고, 관계에 따라 코드를 재사용할 곳이 어딘지 파악한 후 코딩한다
7) 정보 은닉화 : 자바스크립트는 정보 은닉과 관련한 키워드를 제공하지않는다. 그러나 가능하다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | var person = function(arg){ var name; if(arg){//빈문자열 체크, name = arg; } else { name = ""; } this.getName = function(){ return name; }; this.setName = function(arg){ name = arg; }; } /* 혹은 */ var person = function(arg){ var name; if(arg){//빈문자열 체크, name = arg; } else { name = ""; } return { getName : function(){ return name; }, setName: function(arg){ name = arg; } } } | cs |
- 메서드를 거쳐야 인스턴스 프로퍼티 접근, 수정 가능함
- 위의 경우 : 일반 함수 호출을 할 경우(new 키워드 안쓰고) this는 전역객체에 바인딩 : this 바인딩 게시글 참고할 것
- 아래의 경우 : new 키워드, 일반 함수 호출 둘다 return 되는 객체가
- 아래의 경우 : private 멤버가 객체나 배열일 때 수정 가능함
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | var person = function(personInfo){ var obj = personInfo; return { getPersonInfo: function(){ return obj; } } } var jinbro = person({ name: "jinbro", age: 26 }); var jinbroInfo = jinbro.getPersonInfo(); jinbroInfo.name = "jinhyung"; console.dir(jinbro.getPersonInfo()); // Object > age: 26, name: "jinhyung", __proto__ | cs |
- 객체 멤버로 접근하는 메서드의 리턴값(객체 참조값)을 변수에 저장한 후 객체의 멤버값을 변경 가능 : 얕은복사(메모리주소값)
- 객체의 변경을 막기위해 깊은복사 : 하나하나 반복문으로 새로운 객체에 저장(참조값을 복사X -> 값을 복사해줌 - 별개 객체)
- 거듭말하지만 위와 같은 방법은 멤버변수 이름만 같은 단순 객체만 리턴해주는 것 : 프로토타입 객체 활용X(상속X)
8) 정보 은닉 + 상속, 객체지향(클로저)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | var Person = function(){ var name; var F = function(arg){ if(arg){ name = arg; } else{ name = ""; } } F.prototype = { getName: function(){ return name; }, setName: function(arg){ name = arg; } } return F; }(); | cs |
- Person 함수
1) 생성자로 사용될 함수
2) 프로토타입 객체가 정의된 함수를 리턴해줌
3) new를 통해서 실행되는 함수는 2번의 함수(Person은 함수명만 호출해도 실행되서 2번 함수가 리턴)
- name : F 함수가 Person 함수의 name 클로저, F 함수를 리턴하면서 Person의 생명주기는 끝나지만 name 참조는 됨
- F.prototype : 상속을 위한 프로토타입 객체, Person.prototype === 인스턴스.__proto__ // true
- 정리 : 인스턴스 변수는 Person 영역(클로저 - private하게), 상속될 변수, 함수는 Person의 리턴 함수의 프로토타입 객체 영역
[참고자료]
- 개인블로그, PoiemaWeb Javascript Object-Oriented-Programming: http://poiemaweb.com/js-object-oriented-programming
- github, nhnent 자바스크립트 개발 가이드 : https://github.com/nhnent/fe.javascript/wiki/FE-Weekly
- 위키피디아, 객체지향프로그래밍 : https://goo.gl/cyDmph
'javascript' 카테고리의 다른 글
[프로그래밍 기초] 자바스크립트 런타임, 코어와 쓰레드 (0) | 2017.06.12 |
---|---|
[자바스크립트] 비동기처리 Promise (0) | 2017.05.30 |
[API] nodejs 파일 업로드 모듈 (0) | 2017.05.23 |
[웹기본개념] 집에서 웹서버 운영하기 (0) | 2017.05.22 |
[자바스크립트 자료구조] 배열 (0) | 2017.05.22 |
댓글