반응형

자바스크립트에서 상속의 개념은 프로토타입 체인을 이용

chaintest.html

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
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>프로토 타입 체인</title>
</head>
<body>
<pre>
<script type="text/javascript">
var Animal = function() {}
 
Animal.prototype = {
    walk : function() { document.writeln('종종...'); }
};
 
var Dog = function() {};
Dog.prototype = new Animal();
 
Dog.prototype.bark = function() { document.writeln('멍멍!'); }
 
var d = new Dog();
d.walk();
d.bark();
</script>
</pre>
</body>
</html>
cs

위에서 16번 줄의 Dog.prototype = new Animal(); 코드는 Dog객체의 프로토타입에 Animal 추가하고 있다.

이를 이용하여 Dog객체에서 walk()함수를 호출(21번 줄 d.walk() )  있게 된다.

만약 walk() 존재하지 않았다면 Object.prototype(프로토타입 체인의 )까지 거슬로 올라가며 멤버를 찾는다.

 

자바스크립트는 프로토타입에 인스턴스를 설정하여 인스턴스 간에 서로 연결되는 개념을 만들어 상위/하위 개념을 구현하는 것으로 상속 개념을 구현함. 이를 프로토타입 체인이라고 한다.

 

자바스크립트에서 프로토타입 체인은 동적이다.

chainDynamictest.html

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
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>프로토타입 체인</title>
</head>
<body>
<pre>
<script type="text/javascript">
var Animal = function() {}
 
Animal.prototype = {
  walk : function() { document.writeln('종종...'); }
};
 
var SuperAnimal = function() {}
SuperAnimal.prototype = {
  walk : function() { document.writeln('다다다닷!'); }
};
 
var Dog = function() {};
Dog.prototype = new Animal();
var d1 = new Dog();
d1.walk();
 
Dog.prototype = new SuperAnimal();
var d2 = new Dog();
d2.walk();
d1.walk();
</script>
</pre>
</body>
</html>
cs

마지막 d1.walk() 결과는? "종종.." 이다.

자바스크립트의 프로토타입 체인은 인스턴스가 생성된 시점에서 고정되어 후의 변경은 적용되지 않는다.

 

상속 구현 방법

1. 클래스 기반의 상속 구조와 비슷한 개념으로 만드는 방법

inheritance.html

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
38
39
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>클래스와 비슷한 계승</title>
</head>
<body>
<pre>
<script type="text/javascript">
function initializeBase(derive, base, baseArgs) {
    base.apply(derive, baseArgs);
    for(prop in base.prototype) {
        var proto = derive.constructor.prototype;
        if(!proto[prop]) {
            proto[prop] = base.prototype[prop];
        }
    }
}
var Member = function(firstName, lastName){
    this.firstName = firstName;
    this.lastName = lastName;
};
Member.prototype.getName = function(){
    return this.lastName + ' ' + this.firstName;
};
var SpecialMember = function(firstName, lastName, role) {
    initializeBase(this, Member, [firstName, lastName]);
    this.role = role;
}
SpecialMember.prototype.isAdministrator = function(){
    return (this.role == 'Administrator');
};
var mem = new SpecialMember('길동''홍''Administrator');
document.writeln('이름:' + mem.getName());
document.writeln('관리자:' + mem.isAdministrator());
</script>
</pre>
</body>
</html>
 
cs

initializeBase함수가 핵심 개념을 표현하고 있다.

함수는 상속 클래스의 생성자 안에서 호출될 것을 가정하여 정의된 함수

  • 부모 클래스의 생성자를 호출
  • 부모 클래스의 정의된 멤버를 자식 클래스에 복사한다.

initializeBase(현재 인스턴스, 부모 클래스, 인수 배열);

부모 클래스의 생성자를 호출함과 동시에 현재 클래스에 대해 부모 클래스에 정의된 멤버를 복사한다.

인수배열은 부모 클래스의 생성자에 전달하는 인수이며 인수가 없으면 빈배열([]) 전달한다.

 

initializeBase메서드 호출되는 부분 해석

initializeBase(this, Member, [firstName, lastName]); 호출 시

base.apply(derived, baseArgs); 호출함. 다음 값이 각각 인자로 매핑

 

Member this [firstName, lastName]

apply 메서드: Function객체가 제공하는 메서드.

derived 전달받은 this 자식 객체를 의미하며 부모클래스(Member) 생성자를 호출하는 .

부모 생성자 호출에 필요한 값을 배열 리터럴로 전달하고 있는 .

생성된 부모 객체의 멤버를 자식 클래스에서 사용할 있도록 복사하는 동작이 진행된다.

for(prop in base.prototype) {

    var proto = derive.constructor.prototype;

    if(!proto[prop]) {

        proto[prop] = base.prototype[prop];

    }

}

if문을 통해 자식 클래스에 동일한 메서드가 정의되어 있지 않을 경우에만 복사를 하고 있다.

그리고 대입하려는 곳은 인스턴스의 프로토타입(Member) 객체가 아닌 생성자 함수의 프로토타입(SpecialMember) 객체인 것도 알아두자. derived변수에는 자식 클래스의 객체를 참조하고 있으므로 contructor속성으로 생성자를 참조하여 사용하고 있다.

 

주의할

    상속받는 멤버들은 인스턴스화 정적으로 정해진다.

    앞서 확인했던 프로토타입 객체에 대한 실시간 변경을 인식할 없다.

 

    부모 클래스의 메서드를 자식에서 사용하려면

    상속은 부모 기능에 새로운 기능을 추가하는 외에도

    부모 클래스의 메서드를 재정의(오버라이딩)하는 것도 필요할 있다.

    이런 경우 자식 클래스의 메서드로부터 부모 클래스의 메서드를 호출하는 행위가 필요하다.

 

inheritance2test.html

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
38
39
40
41
42
43
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>클래스같은 계승</title>
</head>
<body>
<pre>
<script type="text/javascript">
function initializeBase(derive, base, baseArgs) {
    base.apply(derive, baseArgs);
    for(prop in base.prototype) {
        var proto = derive.constructor.prototype;
        if(!proto[prop]) {
            proto[prop] = base.prototype[prop];
        }
    }
}
 
var Member = function(firstName, lastName){
    this.firstName = firstName;
    this.lastName = lastName;
};
 
Member.prototype.getName = function(){
    return this.lastName + ' ' + this.firstName;
};
 
var SpecialMember = function(firstName, lastName, role) {
    initializeBase(this, Member, [firstName, lastName]);
    this.role = role;
}
 
SpecialMember.prototype.getName = function(){
    var result = Member.prototype['getName'].apply(this, []);
    return result + '(' + this.role + ')';
};
 
var mem = new SpecialMember('길동''홍''Administrator');
document.writeln('이름:' + mem.getName());
</script>
</pre>
</body>
</html>
cs

개념을 보면 부모 클래스의 메서드 기능을 동작시킨 결과를 받아서 자식에서 추가하는 값을 붙여 반환하고 있다.

 

private멤버 정의하기

    private 멤버는 객체지향에서 은닉화 개념을 표현하기 위한 키워드다.

    이는 클래스 내부에서만 직접적인 접근이 가능한 멤버를 의미

    코딩 멤버 데이터 조작의 실수를 막기 위함

    자바스크립트에서 멤버들의 기본 속성은 public이다.

    private 정의하는 구문은 없고 클로저를 활용하여 개념을 표현할 있다.

 

privatetest.html

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
38
39
40
41
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Private멤버</title>
</head>
<body>
<pre>
<script type="text/javascript">
function Triangle() {
  var _base;
  var _height;
  var _checkArgs = function(val) {
    return (!isNaN(val) && val > 0);
  }
  this.setBase = function(base) {
    if(_checkArgs(base)){_base = base;}
  }
  this.setHeight = function(height) {
    if(_checkArgs(height)){_height = height;}
  }
  this.getBase = function() { return _base; }
  this.getHeight = function() { return _height; }
}
 
Triangle.prototype.getArea = function() {
  return this.getBase() * this.getHeight() / 2;
}
 
var t = new Triangle();
t._base = 10;
t._height = 2;
document.writeln('삼각형의 면적:' + t.getArea());
t.setBase(10);
t.setHeight(2);
document.writeln('삼각형의 밑변:' + t.getBase());
document.writeln('삼각형의 높이:' + t.getHeight());
document.writeln('삼각형의 면적:' + t.getArea());
</script>
</pre>
</body>
</html>
cs

private으로 지정할 변수/함수는 생성자 내에 var키워드로 등록한다.

t._base t._height에는 직접 접근할 없게 된다.

따라서 getter setter메서드를 이용하여 내부 멤버에 값을 가져오거나 세팅할 있게 해야 .

이렇게 내부 private멤버에 접근할 있는 공개된 함수를 privileged 메서드라고 한다.(생성자 안에서 정의)

 

setter getter메서드를 AccesorMethod라고 한다.

  • 값을 읽기(쓰기) 전용으로 만들 있다.
  • 참조 데이터를 가공할 있다.
  • 설정 타당성 검증을 있다.

 

private 메서드 -> 생성자에서 정의

public 메서드 -> 프로토타입 객체에서

public 메서드에서 특권이 필요한 멤버 -> 생성자에서 정의

private 멤버를 나타낼 _언더스코어를 붙이는 것이 관례이다.

 

네임스페이스

클래스명, 라이브러리명 등이 중복되는 경우를 막기 위해 지정

이름을 붙여놓은 공간

동일한 이름의 중복을 막기 위해서 사용하게 된다.

자바스크립트는 네임스페이스를 만들 객체를 이용하여 개념을 구현

 

namespacetest.html

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
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>네임스페이스/패키지의 작성</title>
</head>
<body>
<pre>
<script type="text/javascript">
var Wings = function() {};
 
Wings.Member = function(firstName, lastName){
    this.firstName = firstName;
    this.lastName = lastName;
};
 
Wings.Member.prototype = {
    getName : function(){
        return this.lastName + ' ' + this.firstName;
    }
};
 
var mem = new Wings.Member('길동''야마다');
document.write(mem.getName());
</script>
</pre>
</body>
</html>
cs

네임스페이스를 정의하는 방법은 생성자 함수를 생성하는 .

해당 객체에 메서드들을 추가하여 등록하고

호출 활용하면 .

네임 스페이스가 다르면 동일한 이름의 메서드라도 구분이 가능

네임스페이스를 포함한 이름으로 사용할 필요가 있음.

반응형

+ Recent posts