개발일지

코어 자바스크립트 5주차 발표 본문

코어 자바스크립트 스터디

코어 자바스크립트 5주차 발표

박수미/ 2024. 9. 2. 20:10

7장 클래스

자바스크립트는 프로토타입 기반 언어라서 "상속"개념이 존재하지 않습니다. 따라서 ES6에서 클래스문법이 추가되었습니다.

1. 클래스와 인스턴스의 개념 이해

먼저, 객체지향 언어에서 필수로 등장하는 개념인 클래스와 인스턴스에 대해 짚고 넘어가야 합니다.

  • 클래스: 공통 요소를 지니는 집단을 분류하기 위한 개념
  • 인스턴스: 클래스의 속성을 지니는 실존하는 개체

예를 들어, 위 그림처럼 음식이라는 범주안에는 고기, 채소, 과일 등 다양한 것들이 들어갈 수 있습니다. 하위에 각 분류에 속하는 대상들로 또 나눌 수 있고, 과일이라는 범주 아래에는 배, 사과, 바나나, 감, 오렌지 등이 포함됩니다. 여기서 배, 사과, 바나나 등은 직접 만질 수 있고 볼 수 있고 먹을 수 있는 구체적이고 실존하는 사물에 해당합니다. 반면, 음식이나 과일은 어떤 사물들의 공통 속성을 모아 정의한 것일 뿐 직접 만질 수도 볼 수도 없는 추상적인 개념입니다.

출처 코어 자바스크립트

위 그림과 같이 음식, 과일은 모두 집단, 즉 클래스입니다. 음식은 과일보다 상위의 개념이고, 과일은 음식보다 하위의 개념입니다. 여기서 앞의 super-, sub-을 접목해서 상위 클래스(superclass) / 하위 클래스(subclass)로 표현합니다. 그렇다면 과일 분류 하위에 또 다른 분류가 있을 경우에는 클래스 간의 관계는 어떻게 될까요?

출처 코어 자바스크립트

음식은 과일의 superclass입니다. 과일은 음식의 subclass이면서 귤류의 superclass입니다.

감귤, 자몽, 천혜향 등은 구체적인 특징을 가진 개체들입니다. 이처럼 어떤 클래스의 속성을 지니는 실존하는 개체를 일컬어 인스턴스라고 합니다.

2. 자바스크립트의 클래스

프로토타입을 일반적인 의미에서의 클래스 관점에서 접근해 보면, 비슷하게 해석할 수 있는 요소가 있습니다.

생성자 함수 Array를 new 연산자와 함께 호출하면 인스턴스가 생성됩니다. 이때 Array를 일종의 클래스 하고 하면, Array의 prototype 객체 내부의 요소들이 인스턴스에 '상속'된다고 볼 수 있습니다.

프로토타입 체이닝에 의한 참조가 결과적으로는 클래스의 상속과 동일하게 동작하기 때문입니다.

 

인스턴스가 직접 메서드를 호출할 수 있는지 여부에 따라 해당 메서드가 상속되는지 아닌지 확인해 볼 수 있습니다.

// 생성자 함수
const Rectangle = function (width, height) {
	this.width = width;
  	this.height = height;
}

// 프로토타입 메서드 (prototype method)
Rectangle.prototype.getArea = function () {
	return this.width * this.height;
}

// 스태틱 메서드 (static method)
Rectangle.isRectangle = function (instance) {
	return instance instanceof Rectangle && instance.width > 0 && instance.height > 0;
}

// 인스턴스 생성
const rect1 = new Rectangle(3, 4);

console.log(rect1.getArea());              // 12
console.log(rect1.isRectangle(rect1));     // Error
console.log(Rectangle.isRectangle(rect1)); // true

위 예제는 전형적인 생성자 함수와 인스턴스입니다.

생성자 함수의 prototype에 선언된 getArea() 메서드는 프로토타입 체이닝에 의해 인스턴스에 상속되고, 프로토타입 객체에 할당한 메서드는 인스턴스가 마치 자신의 것처럼 호출할 수 있습니다. 이처럼 인스턴스에서 직접 호출할 수 있는 메서드가 바로 프로토타입 메서드라고 하며, 위의 isRectangle() 메서드처럼 인스턴스에서 직접 접근할 수 없는 메서드를 스태틱 하다고 표현합니다.

3. 클래스 상속

클래스 상속은 객체지향에서 가장 중요한 요소 중 하나입니다.

 

우리는 6장 6-2-4절에서 다중 프로토타입 체인에 대해 알아보았습니다. 아래에서 다룰 내용이 클래스 상속의 핵심입니다.

 

- Grade 생성자 함수 및 인스턴스

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 = [];
var g = new Grade(100, 80);

하지만 이때까지는 세부적으로 완벽하게 superclass와 subclass의 구현이 이뤄진 것이 아니기 때문에 length 프로퍼티가 configurable(삭제 가능)하다는 점과, Grade.prototype에 빈 배열을 참조시켰다는 점이 문제가 될 수 있습니다.

 

length 프로퍼티를 삭제한 경우

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}

처음에는 원하는 대로 결과값이 나왔지만, 그런데 12번째 줄에서 length 프로퍼티를 삭제하고 다시 push 한 값이 0번째 인덱스에 들어갔고, length가 1이 됐습니다.

 length 프로퍼티가 삭제 가능했던 이유는 무엇일까요?

내장객체인 배열 인스턴스의 length 프로퍼티는 configurable 속성이 false라 삭제가 불가능하지만, Grade 클래스의 인스턴스는 배열 메서드를 상속하지만 기본적으로는 일반 객체의 성질을 그대로 지니므로 삭제가 가능했기 때문입니다. 그리고 push를 했을 때 0번째 인덱스가 70이 들어가고 length가 다시 1이 될 수 있었던 것은 g.__proto__.length, 즉 Grade.prototype이 빈 배열을 가리키로 있기 때문입니다. push 명령에 의해 자바스크립트 엔진이 g.length를 읽고자 하는데 g.length가 없으니까 프로토타입 체이닝을 타고 g.__proto__.length을 읽어온 것입니다. 빈 배열의 length가 0이므로 여기에 값을 할당하고 length는 1만큼 증가시키라는 명령이 문제없이 동작할 수 있었던 것입니다.

4. ES6의 클래스 및 클래스 상속

ES5와 ES6의 클래스 문법 비교

var ES5 = function (name) {
	this.name = name;
};
ES5.staticmethod = function () {
	return this.name + ' staticMethod';
};
ES5.prototype.method = function () {
	return this.name + ' method';
};
var es5Instance = new ES5('es5');
console.log(ES5.staticMethod());   // es5 staticMethod
console.log(es5Instance.method()); // es5 method

-------------------------------------------------------

var ES6 = class {
	constructor (name) {
    	this.name = name;
    }
  static staticMethod () {
  		return this.name + ' staticMethod';
  }
  method () {
  		return this.name + ' method';
  }
};
var es6Instance = new ES6('es6');
console.log(ES6.staticMethod());   // es6 staticMethod
console.log(es6Instance.method()); // es6 method

기본적으로 생성자 함수와 클래스 문법 모두 스태틱 메서드, 프로토타입 메서드에 동일하게 작동하고 있음을 확인할 수 있습니다.

 

다음은 ES6의 클래스 상속 관련 코드입니다.

var Rectangle = class {
	consturctor (width, height) {
    	this.width = width;
      	this.height = height;
    }
  	getArea () {
    	return this.width * this.height;
    }
};
var Square = class extends Rectangle {
	constructor (width) {
    	super (width, width)
    }
  	getArea () {
    	console.log('size is :', super.getArea());
    }
}
  • 10번째 줄에서 Square를 Rectangle 클래스를 상속받는 SubClass로 만들기 위해 class 명령어 뒤에 단순히 extends Rectangle이라는 내용을 추가했습니다. 이것만으로 상속 관계 설정이 끝납니다.
  • constructor 내부에서 super라는 키워드를 함수처럼 사용할 수 있는데, 이 함수는 SuperClass의 constructor를 실행합니다.
  • constructor 메서드를 제외한 다른 메서드에서는 super 키워드를 마치 객체처럼 사용할 수 있고, 이때 객체는 SuperClass.prototype을 바라보는데, 호출한 메서드의 this는 'super'가 아닌 원래의 this를 그대로 따릅니다.

정리

  • 클래스는 어떤 사물의 공통 속성을 모아 정의한 추상적인 개념이고, 인스턴스는 클래스의 속성을 지니는 구체적인 사례이다.
  • 상위 클래스(superclass)의 조건을 충족하면서 더욱 구체적인 조건이 추가된 것을 하위 클래스(subclass)라고 한다.
  • 프로토타입 메서드 : 클래스의 prototype 내부에 정의된 메서드로, 인스턴스가 마치 자신의 것처럼 호출할 수 있다.
  • 스태틱 메서드 : 클래스(생성자 함수)에 직접 정의된 메서드로, 인스턴스가 직접 호출할 수 없고 클래스에 의해서만 호출될 수 있다.
  • super 키워드로 상위 클래스에 접근할 수 있다.