[코어 자바스크립트] 07_클래스

Mooongs·2022년 6월 26일
0

코어자바스크립트

목록 보기
7/8
post-thumbnail

클래스(Class)

자바스크립트는 6강에서 봤듯이 프로토타입 기반 언어이기 때문에 '상속' 개념이 없고, ES6에서 클래스 문법이 도입되기 전까지 프로토타입을 활용해왔다. 그래서 내용 또한 6장의 연장 선상인 느낌이 있다.

7장 총평: 클래스를 도입해주신 ECMA에 무한한 감사..........


클래스와 인스턴스

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

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

이 책에서는 '음식'이라는 범주를 클래스에 빗대어 설명하고 있다. 음식이라는 클래스는 '먹을 수 있는 것'이라는 공통 요소를 가지고 있으며 하위에는 고기, 채소, 과일 등 다양한 세부 카테고리가 들어올 수 있고 각각이 또 다른 클래스가 되는 것이다. 예를 들어 과일이라는 클래스 밑에는 더 구체성을 갖는 귤류와 같은 하위클래스가 들어올 수 있다. 그리고 귤류의 속하는 실제 개체를 '인스턴스'라고 한다.

위처럼 상위의 개념을 상위 클래스(superclass) 하위의 개념을 하위 클래스(subclass)라고 하며, 하위 개념은 상위 개념을 포함하면서 더 구체적인 개념을 포함하게 된다. 위의 예시에서 보면 음식 → 과일 → 귤류 순으로 구체성을 갖는 하위 클래스가 되며 귤류에 속하는 자몽, 오렌지가 인스턴스라고 할 수 있다.

자바스크립트의 클래스

자바스크립트의 프로토타입 체이닝 개념을 통해서 클래스와 클래스 상속에 관한 개념을 비슷하게 이해할 수 있다. 인스턴스가 직접 메서드를 호출할 수 있는지 여부에 따라서 해당 메서드가 상속되는지 아닌지를 확인해볼 수 있다. 아래 예시는 생성자 함수 Array로 인스턴스를 만든 케이스이다.

// 생성자 함수
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() 메서드처럼 인스턴스에서 직접 접근할 수 없는 메서드를 스태틱하다고 표현한다.

클래스 상속

ES6에서 클래스와 상속 개념이 도입되기 전까지는 프로토타입 체인을 활용해서 클래스 상속을 구현하고 전통적인 객체지향 언어에서의 클래스와 비슷한 형태로 발전시키는 게 큰 목표였다고 한다.

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}

처음에는 원하는 대로 결과값이 나왔지만, length 프로퍼티를 삭제하고 다시 push를 했더니 그 값이 0번째 인덱스에 들어갔고 length가 1이 됐다.
먼저, length 프로퍼티가 삭제 가능했던 이유는 무엇일까?
내장객체인 배열 인스턴스의 length 프로퍼티는 configurable 속성이 false라 삭제가 불가하다. 하지만 배열 메서드를 상속받는 Grade 클래스의 인스턴스는 기본적으로 일반 객체의 성질을 그대로 지니므로 삭제가 가능했던 것이다.
그리고 push 명령을 보고 자바스크립트 엔진이 g.length를 읽고자 하는데 g.length가 없기 때문에 프로토타입 체이닝을 타고 g.__proto__.length, 즉 Grade.prototype.length를 읽어온 것이다. 빈 배열의 length는 0이기 때문에 두번째 push의 결과에 length: 1이라는 결과가 출력된 것이다.

이렇게 ES5까지의 클래스 상속 구현에서는 이외에도 다양한 문제가 발생할 수 있는 여지가 많았고 ES6에서 드디어 클래스 문법이 도입되었다.

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번째 줄에서 상속 관계 설정을 위해 class 명령어 뒤에 extends Rectangle이라는 내용을 추가한 것을 볼 수 있다. Square는 Rectangle 클래스를 상속받는 SubClass라는 것을 의미한다.

constructor 내부에서는 super라는 키워드를 함수처럼 사용해서 SuperClass의 constructor를 실행하고 있다.

constructor 메서드를 제외한 다른 메서드에서는 super 키워드를 마치 객체처럼 사용할 수 있는데, 이때 객체는 SuperClass.prototype을 바라보지만 호출한 메서드의 this는 super가 아니라 원래의 this를 그대로 따른다.


정리

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

0개의 댓글