반응형

자바스크립트는 객체지향 언어

    인스턴스화, 인스턴스 개념은 존재. 클래스가 없고 프로토타입만 존재

 

프로토타입이란 어떤 객체의 원본이 되는 객체

    자바스크립트는 프로토타입 객체를 이용해 새로운 객체를 생성하는 형태

    클래스 베이스 객체지향 언어들(Java, C++ )

    프로토타입 베이스 객체지향(자바스크립트)

    결합도가 다소 느슨한 클래스의 개념

    자바스크립트에서 프로토타입을 클래스라고 지칭하는 경우도 있다.

 

simpletest.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>객체의 정의</title>
</head>
<body>
<pre>
<script type="text/javascript">
var Member = function() {};
 
var mem = new Member();
</script>
</pre>
</body>
</html>
cs

형태가 자바스크립트에서의 클래스이다.

자바스크립트는 Function객체에 클래스의 개념을 부여하는 .

 

생성자로 초기화 하기

    new 이용하여 객체를 생성하는 구문을 생성자호출 이라고 한다.

    멤버의 초기화에 사용되는 메서드이다.

    생성자의 이름은 보통 대문자로 시작(클래스 개념을 사용하는 변수는 대문자로 시작하도록)

    생성자는 반환값 지정 불가

 

객체의 프로퍼티와 메서드

simple2test.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>객체의 정의</title>
</head>
<body>
<pre>
<script type="text/javascript">
var Member = function(firstName, lastName){
    this.firstName = firstName;
    this.lastName = lastName;
    this.getName = function(){
        return this.lastName + ' ' + this.firstName; 
    }
};
 
var mem = new Member('길동''홍');
document.writeln(mem.getName());
</script>
</pre>
</body>
</html>
cs

 

위에서 사용된 this변수는 생성자에 의해 생성되는 인스턴스(객체)자체를 의미(참조)한다.

프로퍼티는 문자열, 정수, 날짜, 함수 객체를 지정할 있다.

자바스크립트는 엄밀히 말해서 메서드(클래스 멤버 함수) 없다.

함수 객체로써의 값이 프로퍼티의 메서드로 인지되는 .( 예제의 getName 같은 선언)

 

동적으로 메서드 추가

    자바스크립트의 객체는 new 이용하여 인스턴스를 생성한 후에도 메서드를 추가할 있다.

dynamictest.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>객체의 정의</title>
</head>
<body>
<pre>
<script type="text/javascript">
var Member = function(firstName, lastName){
    this.firstName = firstName;
    this.lastName = lastName;
};
 
var mem = new Member('길동''홍');
mem.getName = function(){
    return this.lastName + ' ' + this.firstName; 
}
 
document.writeln(mem.getName());
</script>
</pre>
</body>
</html>
cs

 

다음과 같은 상황에서는 에러가 발생한다.

dynamic2test.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 Member = function(firstName, lastName){
    this.firstName = firstName;
    this.lastName = lastName;
};
 
var mem = new Member('길동''홍');
mem.getName = function(){
    return this.lastName + ' ' + this.firstName; 
}
 
document.writeln(mem.getName());
 
var mem2 = new Member('감찬''강');
document.writeln(mem2.getName());
</script>
</pre>
</body>
</html>
cs

 

mem2 객체에는 getName메서드가 없음.

동적인 메서드를 추가하는 개념은 Member클래스가 아닌 생성된 인스턴스에 메서드가 추가되는 .

자바스크립트는 프로토타입 베이스 객체지향이라고 했다.

이는 동일한 클래스로 객체를 생성하더라도 서로 다른 멤버를 가질 있다는 .

delete연산자로 객체의 멤버를 삭제할 수도 있다.

 

메서드는 프로토타입으로 선언할 prototype

    객체에 메서드를 추가하는 경우 앞의 예제는 생성자에서 추가하는 형태.

    이는 메서드 수에 따라 소비되는 메모리가 많아진다.

    위의 예제에서 getName 보면 객체가 생성될 때마다 동일한 기능을 가지는 함수 객체가 각각 만들어지게 된다.

    기능이 달라지는 형태라면 모르겠지만 동일한 기능의 객체가 메모리에 많이 생성되는 것은 비효율 적이다.

    이를 해결하기 위해 prototype이라는 프로퍼티를 지원하고 있다.

    동일한 클래스로 생성된 객체들이 prototype 프로퍼티를 참조하게 되어 있다.

prototypetest.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>객체의 정의</title>
</head>
<body>
<pre>
<script type="text/javascript">
var Member = function(firstName, lastName){
    this.firstName = firstName;
    this.lastName = lastName;
};
 
Member.prototype.getName = function(){
    return this.lastName + ' ' + this.firstName;
};
 
var mem = new Member('길동''홍');
document.writeln(mem.getName());
</script>
</pre>
</body>
</html>
cs

 

위와 같은 개념을 보면 자바스크립트는 클래스라는 개념이 없고 프로토타입이라는 기본 객체에 필요한 멤버들을 추가하는 형태로 객체지향을 구현하고 있다.

 

프로토타입 객체를 이용하여 공통 메서드를 정의하는 경우의 가지 장점

1. 메모리 사용량을 감소시킨다.

    프로토타입 객체의 내용은 베이스 객체로부터 암묵적인 참조를 하는 것일 인스턴스에 복사하지 않는다.

    자바스크립트에서 객체의 멤버를 호출 하는 경우

  • 인스턴스에 해당 이름을 가진 멤버 존재 여부 확인(있다면 동작)
  • (없다면)암묵적인 참조를 통해 프로토타입 객체에서 해당 이름의 멤버를 찾는다.

   생성자를 이용하여 메서드를 정의하는 것에 비해 메모리의 쓸데없는 소모를 줄인다.

 

2. 멤버의 추가/변경을 인스턴스가 실시간으로 인식

    인스턴스에 멤버를 복사하지 않는다는 것은 프로토타입 객체의 변경을 인스턴스에서 동적으로 인식한다는 .

dynamic3test.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
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>객체의 정의</title>
</head>
<body>
<pre>
<script type="text/javascript">
var Member = function(firstName, lastName){
    this.firstName = firstName;
    this.lastName = lastName;
};
 
var mem = new Member('길동''홍');
 
Member.prototype.getName = function(){
    return this.lastName + ' ' + this.firstName;
};
 
document.writeln(mem.getName());
</script>
</pre>
</body>
</html>
cs

 

예제는 getName메서드를 인스턴스를 생성한 후에 추가하고 있지만 문제없이 호출이 가능함.

(암묵적 참조)

 

프로토타입 객체의 불가사의? - 프로퍼티의 설정

    프로토타입 객체로 프로퍼티를 선언한다면?

prototype2test.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>객체의 정의</title>
</head>
<body>
<pre>
<script type="text/javascript">
var Member = function(){ };
Member.prototype.gender = '남자';
 
var mem1 = new Member();
var mem2 = new Member();
document.writeln(mem1.gender + '|' + mem2.gender);
mem2.gender = '여자';
document.writeln(mem1.gender + '|' + mem2.gender);
</script>
</pre>
</body>
</html>
cs

 

위와 같은 경우 암묵적 참조를 알고있다면 결과를 문제없이 이해할 있다.

mem1, mem2 gender프로퍼티가 없을 때는 암묵적으로 프로토타입의 gender 찾는다.

mem2 gender값을 정의하면 mem2 sex값을 갖고 있으므로 프로토타입의 gender 참조하지 않는다.

mem1 여전히 gender프로퍼티가 없으므로 암묵적으로 프로토타입의 gender 사용한다.

 

따라서 객체의 멤버를 정의할 때는 다음과 같은 방식을 권장한다.

  • 프로퍼티 선언 -> 생성자
  • 메서드의 선언 -> 프로토타입

 

정적 프로퍼티/정적 메서드를 정의하려면?

    정적 멤버는 인스턴스 없이 클래스이름으로 사용이 가능한 멤버

    생성자(객체) 직접 정의한다.

statictest.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 Area = function() {};
 
Area.version = '1.0';
 
Area.triangle = function(base, height) {
  return base * height / 2;
}
Area.diamond = function(width, height) {
  return width * height / 2
}
 
document.writeln('Area클래스의 버전:' + Area.version);
document.writeln('삼각형의 면적:' + Area.triangle(53));
var a = new Area();
document.writeln('마름모의 면적:' + a.diamond(102));
</script>
</pre>
</body>
</html>
cs

 

인스턴스로 정적 메서드를 호출하면 에러!

 

정적 프로퍼티는 기본적으로 읽기전용으로 사용할 .

정적 메서드는 this 없다.(this 의미가 없다)

 

프로토타입 객체의 불가사의? - 프로퍼티의 삭제

     프로토타입의 멤버를 삭제할 때에도 인스턴스 단위로 처리된다.

prototype3test.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
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>객체의 정의</title>
</head>
<body>
<pre>
<script type="text/javascript">
var Member = function(){ };
 
Member.prototype.gender = '남자';
 
var mem1 = new Member();
var mem2 = new Member();
document.writeln(mem1.gender + '|' + mem2.gender);
mem2.gender = '여자';
document.writeln(mem1.gender + '|' + mem2.gender);
delete mem1.gender
delete mem2.gender
document.writeln(mem1.gender + '|' + mem2.gender);
</script>
</pre>
</body>
</html>
cs

 

인스턴스 멤버의 추가 삭제는 프로토타입에 영향을 주지 않는다.

만약 다음과 같이 프로토타입의 멤버를 삭제하려는 경우에는 모든 인스턴스에 영향을 준다.

    delete Member.prototype.gender

인스턴스 단위로 프로토타입 멤버를 삭제하고 싶은 경우 다음과 같은 편법을 이용할 수는 있다.

undefinedtest.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>객체의 정의</title>
</head>
<body>
<pre>
<script type="text/javascript">
var Member = function(){ };
 
Member.prototype.gender = '남자';
 
var mem1 = new Member();
var mem2 = new Member();
document.writeln(mem1.gender + '|' + mem2.gender);
mem2.gender = undefined;
document.writeln(mem1.gender + '|' + mem2.gender);
</script>
</pre>
</body>
</html>
cs

 

방법은 멤버를 삭제하는 것이 아닌 undefined값으로 해당 프로퍼티를 지정하는 것일 .

다음과 같이 객체 멤버를 확인할 경우 gender프로퍼티는 존재한다.

undefined2test.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>객체의 정의</title>
</head>
<body>
<pre>
<script type="text/javascript">
var Member = function(){ };
Member.prototype.gender = '남자';
 
var mem = new Member();
mem.gender = undefined;
for (var key in mem) {
  document.writeln(key + ":" + mem[key]);
}
</script>
</pre>
</body>
</html>
cs

 

객체 리터럴로 프로토타입 정의하기

    앞에서의 예제는 . 연산자를 이용하여 프로토타입에 멤버를 추가했다.

    멤버의 수가 많을 경우 코드가 장황해지므로 가독성도 떨어지게 된다.

    이를 해결하기 위해 객체리터럴을 이용하여 정의하는 방법을 사용할 있다.

 

literal.html - . 이용하여 직접 정의하는 방법

literaltest.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 Member = function(firstName, lastName){
    this.firstName = firstName;
    this.lastName = lastName;
};
 
Member.prototype.getName = function(){
    return this.lastName + ' ' + this.firstName;
};
 
Member.prototype.toString = function(){
    return this.lastName + this.firstName;
};
var mem = new Member('길동''홍');
document.writeln(mem.getName());
document.writeln(mem.toString());
</script>
</pre>
</body>
</html>
cs

 

 

예제) literal2.html - 리터럴을 이용하여 정의하는 방법

literal2test.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
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>객체의 정의</title>
</head>
<body>
<pre>
<script type="text/javascript">
var Member = function(firstName, lastName){
    this.firstName = firstName;
    this.lastName = lastName;
};
 
Member.prototype = {
    getName : function(){return this.lastName + ' ' + this.firstName;},
    toString : function(){return this.lastName + this.firstName;}
};
var mem = new Member('길동''홍');
document.writeln(mem.getName());
document.writeln(mem.toString());
</script>
</pre>
</body>
</html>
cs

 

리터럴을 사용할 경우의 장점

  • Member.prototype.~ 같은 표현이 최소한으로 사용됨
  • 객체이름이 변경되는 경우 영향의 범위가 한정적이 된다.
  • 동일한 객체의 멤버 정의가 하나의 블록에 작성되므로 가독성이 향상된다.

 

반응형

+ Recent posts