개발공부/자바스크립트

[JavaScript] 스터디 10일차_ 클래스(1) 개념 및 상속

햄❤️ 2021. 7. 13. 13:18
728x90

1.1 클래스의 개념

 - 자바스크립트는 프로토타입 기반 언어라 "상속" 개념이 존재하지 않는다. 클래스와 비슷하게 흉내내는 여러 기법(프로토타입) 등을 사용하다 결국 🔥ES6에서 클래스 문법이 추가 되었다.

 - 클래스는 공통 요소를 지니는 집단을 분류하기 위한 개념이다. 

 - 클래스의 속성을 지니는 실존하는 개체를 인스턴스(instance)라고 한다. 어떤 클래스에 속한 개체는 그 클래스의 조건을 모두 만족하므로 그 클래스의 구체적인 예시, 즉 인스턴스가 된다. 

 - 프로그래밍에서는 클래스를 바탕으로 인스턴스를 만들 때, 비로소 어떤 개체가 클래스의 속성을 지니게 된다. 또한 한 인스턴스는 하나의 클래스만을 바탕으로 만들어진다. 
 * 즉 클래스가 먼저 정의돼야만 그로부터 공통적인 요소를 지니는 개체를 생성할 수 있다.

 

1.2 자바스크립트의 클래스

- 생성자 함수 Array를 new 연산자와 함께 호출하면 인스턴스가 생성된다. 이 때 Array를 일종의 클래스라고 하면, Array의 prototype 객체 내부 요소들이 인스턴스에 상속된다. (엄밀히는 참조)

- 인스턴스에 상속되는지 여부에 따라 스태틱 멤버(static member)와 인스턴스 멤버(instance member)로 나뉜다. 인스턴스 멤버는 인스턴스에도 직접 메서드를 정의할 수 있기 때문에 prototype에 정의한 메서드인지, 인스턴스에 정의한 메서드인지 혼란을 야기해, 프로토타입 메서드(prototype method)라고 부른다.

 

출처: https://hdw0903.github.io/2020/05/11/%ED%81%B4%EB%9E%98%EC%8A%A4-Class-Core-JavaScript/

 

1.2.1 스태틱 메서드, 프로토타입 메서드 예제_1번

 - rect1은 Rectangle 함수를 new 연산자와 함께 호출해서 생성된 인스턴스이다. rect1.__proto__ === Rectangle.prototype 이므로 rect1.(__proto__).getArea() === Rectangle.prototype.getArea() 이므로 3 * 4 = 12가 출력된다. 이렇게 인스턴스에서 직접 호출할 수 있는 메서드가 프로토타입 메서드이다. 

 - rect1은 isRectangle 이라는 메서드에 접근하고 싶지만 rect1.__proto__에 없고, 그 상위 __proto__에도 없으므로 typeError가 발생한다. 이렇게 인스턴스에서 직접 접근할 수 없는 메서드를 스태틱 메서드라고 한다. 

 - 스태틱 메서드는 생성자 함수를 this로 해야만 호출할 수 있다. Rectangle.isRectangle(rect1);

//생성자
var Rectangle = function(width, height){
	this.width = width;
    this.height = height;
};
//프로토타입 메서드 (상속o) 
Rectangle.prototype.getArea = function(){
	return this.width * this.height;
};
//스태틱 메서드 (상속x)
Rectangle.isRectangle = function(instance){
	return instance instanceof Rectangle && instance.width > 0 && instance.height > 0;
};

var rect1 = new Rectangle(3,4)
console.log(rect1.getArea()); //12 출력
console.log(rect1.isRectangle(rect1)); //TypeError: rect1.isRectangle is not a function
console.log(Rectangle.isRectangle(rect1)); //true 출력

rect1의 __proto__에는 isRectangle이 없다. === 스태틱 메서드

 

1.3 클래스 상속 

  1.3.1 기본 클래스 상속 구현

    1.3.1.1 Grade 생성자 함수 및 인스턴스 예제 _ 1번

      -  length 프로퍼티를 삭제하고 push 했더니, push한 값이 0번째 인덱스에 들어가고, length가 1이 되었다. 내장 객체인 배열 인스턴스의 length 프로퍼티는 configurable(삭제가능) 속성이 false라 삭제 불가능하지만, Grade 클래스의 인스턴스는 배열 메소드를 상속하지만 기본적으로 일반 객체의 성질을 그대로 지니므로 삭제 가능하여 문제가 된다. 

     - 왜 0에 70이 들어갔을까? Grade.prototype이 [] 빈 배열을 가리키고 있기 때문에! push 명령에 js엔진이 g.length를 읽고자 하는데 없으니 프로토타입 체이닝을 타고 g.__proto__.length를 읽어왔다. 빈 배열의 length는 0이었으니 70을 push하며 1로 증가했다. 

var Grade = function(){
	var args = Array.prototype.slice.call(arguments);
    for (var i=0; i < args.length; i++){
    	this[i] = args[i];
    }
    this.length = args.length;
};

Grade.prototype = []; 
// Grade의 prototype의 배열의 인스턴스를 할당하며 유사배열객체가 배열의 메소드를 사용할 수 있게되었다.
var g = new Grade(100, 80);

g.push(90);
console.log(g); // Grade {0:100, 1:80, 2:90, length:3}

delete.g.length;
g.push(70);
console.log(g); // Grade {0:70, 1:80, 2:90, length: 1}

 

 


이렇게라면 예기치 않은 오류가 발생한다! 다른 예제를 살펴보자

 

  1.3.1.2  Rectangle.Square 클래스 예제 _ 2번

         - Rectangle과 Square 클래스의 width가 공통요소이고, getArea는 내용이 비슷하다. 만약 Square에서 width 프로퍼티만 쓰지 않고 height 프로퍼티에 width값을 부여하면 getArea를 아래처럼 고칠 수 있다. 

var Rectangle = function(width, height) {
	this.width = width;
    this.height = height;
};

Rectangle.prototype.getArea = function(){
	return this.width * this.height;
};

var rect = new Rectangle(3,4);
console.log(rect.getArea()); //12 출력

var Square = function(width){
	this.width = width;
};

Square.prototype.getArea = function(){
	return this.width * this.width;
};

var sq = new Square(5);
console.log(sq.getArea()); //25 출력

 

1.3.1.2 Rectangle.Square 클래스 변형 예제 _ 3번

      - Square 클래스는 Rectangle의 하위 클래스로 삼고, getArea 메서드는 동일한 동작을 하므로 상위 클래스에서만 정의하고 하위클래스에서는 상속만 받으면 된다. 하위 클래스에서는 해당 메서드를 상속하면서 height대신 width를 넣어주면 된다.   

var Square = function(width){
	this.width = width;
    	this.height = width;
};

Square.prototype.getArea = function(){
	return this.width * this.height;
};

 

  1.3.1.2 Rectangle을 상속하는 Square 클래스 예제 _ 4번

      - Square 생성자 함수 내부에서 Rectangle 생성자 함수를 함수로써 호출했다.
           call(this, width, width)에서 height자리에 width 인자를 전달했다. 메서드를 상속하기 위해 Square의 프로토타입 객체에 Rectangle의 인스턴스를 부여했다.   

var Square = function(width){
	Rectangle.call(this, width, width);
    //this는 Square을 바라본다.
};
Square.prototype = new Rectangle();
console.dir(sq);

 

- sq는 Square의 인스턴스임을 첫 줄에서 보여준다. width, height가 5로 정상적으로 들어가있고, __proto__에는 Rectangle의 인스턴스임을 표시한다. 이어서 width, height는 undeifined이다. 왜? Square.prototype에 값이 존재하기 때문이다.(!)

 

 

 

  - Constructor가 여전히 Rectangle을 바라보는것도 문제이다. sq.constructor로 접근하면 프로토타입 체이닝을 따라 sq.__proto__.__proto__, 즉 Rectangle.prototype에서 찾게되고, 이는 Rectangle을 가리킨다. 

 

 

 - 이처럼 하위 클래스로 삼을 생성자 함수의 prototype에 상위 클래스의 인스턴스를 부여하는 것만으로도 기본적인 메서드 상속은 가능하지만, 다양한 문제가 발생한다. (2)에서는 이런 문제를 해결하는 방법을 배워보자. 

 

 

정리

🌈 자바스크립트는 프로토타입 기반 언어라서 클래스 및 상속 개념은 존재하지 않지만 프로토타입 기반으로 클래스와 비슷하게 동작하게끔 하는 다양한 기법들이 도입돼 왔다.

 

🌈 클래스는 어떤 사물의 공통 속성을 모아 정의한 추상적인 개념이고, 인스턴스는 클래스의 속성을 지닌 구체적인 사례이다. 상위클래스(superclass)의 조건을 충족하며, 더욱 구체적인 조건이 추가된 것을 하위 클래스(subclass)라고 한다.

 

🌈 클래스의 prototype 내부에 정의된 메서드를 프로토타입 메서드라고 하며, 이들은 인스턴스가 마치 자신의 것처럼 호출할 수 있다. 한편 클래스(생성자 함수)에 직접 정의한 메서드를 스태틱 메서드라고 하며, 이들은 인스턴스가 직접 호출할 수 없고 클래스(생성자 함수(에 의해서만 호출할 수 있다. 

 

728x90