본문 바로가기
javascript

[자바스크립트] 객체지향 프로그래밍하기

by jinbro 2017. 5. 26.
[프로토타입 기반 객체지향]
- 자바스크립트는 클래스가 존재하지않음
- 객체지향 프로그래밍을 할 수 있음 : 객체, 인스턴스 변수/메서드, 정보은닉(클로저), 상속(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-Programminghttp://poiemaweb.com/js-object-oriented-programming
- github, nhnent 자바스크립트 개발 가이드 : https://github.com/nhnent/fe.javascript/wiki/FE-Weekly

- 위키피디아, 객체지향프로그래밍 : https://goo.gl/cyDmph




댓글