Notice
Recent Posts
Recent Comments
Link
«   2024/09   »
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
Tags
more
Archives
Today
Total
관리 메뉴

재훈재훈

상속 본문

Computer Engineering/JavaScript

상속

jaehoonx2 2018. 4. 7. 17:49

자바스크립트를 공부하기 전에 '자바스크립트는 자바와

아무 관련 없는 완전히 다른 언어이다' 라는 말을 들은 적이 많았다.

그러나 공부하다 보니 같은 객체지향언어라는 공통점이 있고,

비슷한 개념들이 서로 통용되는 경우가 많다.

오히려 이런 점들이 나한테는 자바스크립트를 공부함에 있어서 방해가 된다.

자바보다 좀 더 자유분방한 객체지향언어라는 느낌을 많이 받는다.

특히 상속과 생성자 부분에서 더욱 혼란스럽다.

그래서 자바와의 구별점을 나름대로 그어보려 한다.



자바스크립트의 객체 생성 과정

1. new 연산자를 통해 빈 객체를 생성한다

이 부분은 자바와 유사함

 

2. 생성자 함수는 this 키워드를 통해 초기화를 진행한다

자바에서 this.var는 인스턴스 자신을 가르킨다는 점에서 좀 다른 것 같다.

this보다는 this()와 느낌이 비슷하다

생성되는 객체 자신의 멤버를 초기화한다고 생각하면 이해가 된다.

 

3. 새로운 객체의 prototype 프로퍼티에 생성자 함수의 프로퍼티 값이

   전달되어 객체와 생성자 함수는 동일한 프로토타입 객체를 참조함

(이게 무슨 얘긴지 이해가 안 된다...)

객체를 생성하면 객체 생성자 함수의 프로토타입 객체(object 객체)에 정의된 멤버를 상속한다

constructor, toString, ValueOf 등이 object 객체의 멤버이다

 

4. this가 가리키는 객체를 반환(초기화 완료된 객체를 의미)





프로토타입 체인과 프로토타입 멤버 상속

"모든 함수는 Function 객체의 프로토타입 멤버를 상속하고,

모든 함수의 프로토타입 객체는 Object 객체의 프로토타입 객체를 상속하며,

모든 생성자 함수의 객체는 Object 객체의 프로토타입 객체를 상속함."

 

이 문장이 너무 어렵게 느껴진다. '모든 객체는 Object 객체와 다 연결되어 있다'

라는 의미로 이해되는데 이게 맞는지 잘 모르겠다.

아직은 프로토타입에 대한 개념 이해가 많이 부족한 것 같다.

 

 

다음 예제는 프로토타입 멤버 상속에 대한 예제이다.

, 부모 객체의 메서드를 어떻게 상속받는가에 대한 예제다.

 

(예제 출처 : sk tacademy <javascript 기본> 강의, 임정환 강사

var Car = function(){};
Car.prototype = {                                // Car 객체의 메소드 정의 - 프로토타입 객체에 하고 있음
startEngine : function(){
document.writeln('시동을 겁니다...<br/>');
},
accelerate : function(){
document.writeln('속도를 올립니다...<br/>');
},
decelerate : function(){
document.writeln('속도를 줄입니다...<br/>');
},
stopEngine : function(){
document.writeln('시동을 끕니다...<br/>');
}
};



var K5 = function(){};

K5.prototype = new Car();

K5.prototype.constructor = K5;

K5.prototype.startNavigation = function(){
document.writeln('네비게이션 안내를 시작합니다...<br/>');
};
K5.prototype.stopNavigation = function(){
document.writeln('네비게이션 안내를 종료합니다...<br/>');
};

var k5 = new K5();
k5.startEngine();
k5.startNavigation();
k5.accelerate();
k5.decelerate();
k5.stopNavigation();
k5.stopEngine();

document.writeln('<br/>');

    



여기서 중요한 부분은 바로 여기다.

var K5 = function(){};
K5.prototype = new Car();
K5.prototype.constructor = K5;


K5 생성자 함수의 프로토타입 객체는 new 연산자와 Object 생성자 함수 호출을 통해

생성된 객체이며 프로토타입 객체의 constructor 프로퍼티(K5.prototype.constructor) K5를 참조한다.

 

K5() 생성자 함수의 prototype 프로퍼티가

new 연산자와 Car() 생성자 호출을K5.prototype new Car(); )

통해 생성된 객체를 참조하면 Car() 생성자 함수의 프로토타입 멤버를 상속하게 된다.

그러나 프로토타입의 객체가 Car() 생성자 함수를 통해서 만들어져서

constructor 프로퍼티(생성자가 아님, 프로퍼티임!) Car() 생성자 함수를 참조한다.

따라서 constructor 프로퍼티를 K5 생성자 함수로 바꿔주어야 한다K5.prototype.constructor K5)




apply() call() - 객체 멤버 상속

apply() call() Function.prototype의 메소드다. 즉 모든 Function(함수) 이 두 함수를 쓸 수 있다.

이 두 함수를 정의하자면, 쉽게 말해서 다른 객체의 메소드를 가져다 쓰는 것이다.

자바로 치면 super로 부모 클래스의 멤버를 가져다 쓰는 것과 비슷한 느낌인 것 같기도 하다.

 

사용법

func.apply(obj, [arg1, arg2, .., argn]);              

func.call(obj, arg1, arg2, arg,3 ..., argn); 

 

func - 가져다 쓸 대상 메소드

obj - 메소드를 사용할 객체, 지정하지 않으면 전역객체를 가르킨다.

argn - 메소드의 인자목록, apply는 인자목록을 배열로, call은 다이렉트로 전달한다.

 

 

 

 

아까의 예제 내용을 조금 변형시킨 예제이다.

 

(예제 출처 : sk tacademy <javascript 기본> 강의, 임정환 강사)

var Car = function(f){            // Car의 생성자 함수
this.fuel = f;
this.velocity = 0;
this.isDriving = false;
};

Car.prototype = {                 // Car의 프로토타입 객체
startEngine : function(){
this.isDriving = true;
this.fuel -= 5;
document.writeln('Car: 시동을 겁니다... (isDriving: ' + this.isDriving + ', fuel: ' + this.fuel + ')<br/>');
},
accelerate : function(){ ... },
decelerate : function(){ ... },
stopEngine : function(){ ... }
};

var K5 = function(f, m){
// 객체 멤버 상속
Car.apply(this, [f]);

// Car.call(this, f);
this.model = m;
};

K5.prototype = new Car();
K5.prototype.constructor = K5;
delete K5.prototype.fuel;
delete K5.prototype.velocity;
delete K5.prototype.isDriving;

K5.prototype.accelerate = function(){
this.velocity += 10;
this.fuel -= 5;
document.writeln('K5 ' + this.model + ': 속도를 올립니다++++ (velocity: ' + this.velocity + ', fuel: ' + this.fuel + ')<br/>');
};
K5.prototype.decelerate = function(){ ... };
K5.prototype.startNavigation = function(){ ... };
K5.prototype.stopNavigation = function(){ ... };


var k5 = new K5(1000, '2013년형');            // 상위 생성자 함수의 멤버를 new 연산자와 하위 생성자 함수를 통해

// 생성할 객체의 멤버로 추가하는 객체 멤버 상속을 수행함
document.writeln('>>> k5.fuel: ' + k5.fuel + '<br/>');
k5.startEngine();
document.writeln('>>> k5.fuel: ' + k5.fuel + '<br/>');
k5.startNavigation();
for (var i = 0; i < 5; i++)
k5.accelerate();
document.writeln('>>> k5.fuel: ' + k5.fuel + '<br/>');
for (var i = 0; i < 5; i++)
k5.decelerate();
document.writeln('>>> k5.fuel: ' + k5.fuel + '<br/>');
k5.stopNavigation();
document.writeln('>>> k5.fuel: ' + k5.fuel + '<br/>');
k5.stopEngine();
document.writeln('>>> k5.fuel: ' + k5.fuel + '<br/>');

document.writeln('<br/>');




이 코드에서 가장 중요한 부분은 바로 이 부분이다.

var K5 = function(f, m){
Car.apply(this, [f]);     // Car.call(this, f);

this.model = m;
};

Car.apply(this, [f]);에서 이 this가 매우 중요한데,

여기서  this K5 생성자 함수를 가르킨다.

그리고 두번째 인자에는 하수에 전달할 인수 집합으로 배열을 전달했다.

Car.apply 대신 Car.call(this, f);를 써도 같은 의미이다.

배열로 전달하느냐 그냥 보내느냐의 차이다.

이제 K5 생성자 함수는 Car 생성자 함수의 메서드를 상속받아 사용할 수 있다.


K5.prototype = new Car();

K5.prototype.constructor = K5;
delete K5.prototype.fuel;
delete K5.prototype.velocity;
delete K5.prototype.isDriving;


하위 생성자 함수의 프로토타입 객체(K5.prototype) new Car()를 통해 생성된 객체인데,

이 객체에는 이미 상속을 통한 프로퍼티를 가지고 있으므로 프로토타입 객체(K5.prototype)

프로퍼티를 제거하는 과정이 필요함. (중복 제거)

또한 첫번째 예제에도 있었지만프로토타입 객체가 Car() 생성자 함수를 통해 만들어졌기 때문에

프로토타입 객체의 constructor 프로퍼티(이거 생성자가 아니라 프로퍼티!!) Car() 생성자 함수를 참조한다.

때문에 K5 생성자 함수를 참조하도록 바꿔줘야 한다.

 

 

 

 

 

자바스크립트 상속, 진짜 이 부분이 고비였다. 헷갈리는 건 둘째 치고라도,

이것도 되고 저것도 되는, 좋게 말하면 유연하고 자유롭지만 나쁘게 말하면 정말 산만한

자바스크립트의 특징 때문에 애를 먹었다. 이렇게 글로 써내려가니 조금은 정리가 되는 것 같다.

'Computer Engineering > JavaScript' 카테고리의 다른 글

getElementById()  (0) 2018.11.16
this  (0) 2018.05.15
constructor 프로퍼티  (0) 2018.04.07
prototype 프로퍼티  (0) 2018.04.07
클로저  (0) 2018.04.07