일시: 2023.01.08
참석자:
금주 스터디는 유효주님께서 발표해주셨습니다.
7.클래스
자바스크립트는 프로토타입 기반 언어라서 ‘상속’ 개념이 존재하지 않음
그래서 ES6에는 클래스 문법이 추가됨 , 다만 ES6의 클래스에서도 일정 부분은 프로토타입을 활용하고 있기 때문에 ,ES5 체제하에서 클래스를 흉내내기 위한 구현방식을 학습하는 것은 여전히 의미 있음
01. 클래스와 인스턴스의 개념 이해
어떤 클래스의 속성을 지니는 실존하는 개체를 인스턴스(instance)
라고 함
인스턴스를 어떤 조건이 부합하는 구체적인 예시로 보면 됨. 이때 조건이 곧 클래스를 의미함
- 프로그래밍 언어상에서는 사용자가 직접 여러 가지 클래스를 정의해야하며, 클래스를 바탕으로 인스턴스를 만들 때 비로소 어떤 개체가 클래스의 속성을 지니게 됨
- 또한 한 인스턴스는 하나의 클래스만을 바탕으로 만들어짐
- 어떤 인스턴스가 다양한 클래스에 속할 수는 있지만 이 클래스들은 모두 인스턴스 입장에선는 ‘직계존속’임 (중첩으로 여러 클래스들에 속한 게 아니라는 뜻인듯, 음식-과일-사과 이렇게가 아니라고)
- 프로그래밍 언어에서의 클래스는 현실세게에서의 클래스와 마찬가지로 ‘공통요소를 지니는 집단을 분류하기 위한 개념’이라는 측면에서는 일치하지만, 클래스가 먼저 정의돼야만 그로부터 공통적인 요소를 지니는 개체들을 생성할 수 있음
02. 자바스크립트의 클래스
생성자 함수 Array를 new 연산자와 함께 호출하면 인스턴스가 생성됨
- 이때 Array를 일종의 클래스라고 하면 , Array의 prototype 객체 내부 요소들이 인스턴스에 ‘상속’된다고 볼 수 있음 ( 엄밀히 말하면 상속은 아니고 프로토타입 체이닝에 의함 참조 - 그래도 결과적으로 동일하게 동작함)
- 한편 Array 내부 프로퍼티들 중 prototype 프로퍼티를 제외한 나머지는 인스턴스에 상속되지 않습니다.
인스턴스에 상속되는지(인스턴스가 참조하는지) 여부에 따라 스태틱 멤버(static member)와 인스턴스 멤버(instance member)로 나뉜다.
- 이 분류는 다른 언어의 클래스 구성요소에 대한 정의를 차용한 것으로서 클래스 입장에서 사용 대상에 따라 구분한 것
- 그런데 자바스크립트에서는 인스턴스에서도 직접 메서드를정의할 수 있기 때문에 ‘인스턴스 메서드’라는 명칭은 프로토타입에 정의한 메서드를 지칭하는 것인지 인스턴스에 정의한 메서드를 지칭하는 것인지에 대해 혼란스러울 수 있음 → 따라서 자바스크립트의 특징을 살려 메서드(prototype method)라고 부르는 편이 더 좋을 것
//생성자
var Rectangle = function (width , height){
this.width = width;
this.height = height;
};
//(프로토타입)메서드
Rectangle.prototype.getArea= function(){
return this.width * this.height;
};
//스태틱 메서드
Rectangle.isRectangle = function(instance){
return instance instanceof Rectangle &&
instance.width > 0 && instance.height > 0;
};
(1) var rect1 = new Rectangle(3,4)
(2) console.log(rect1.getArea()); //12(0)
(3) console.log(rect1.isRectangle(rect1)); //Error(x)
(4) console.log(Rectangle.isRectangle(rect1)); //true
- (1) - Rectangle 함수를 new 연산자와 함께 호출해서 생성된 인스턴스를 rect1에 할당함. 이제 이 인스턴스에는 width , height 프로퍼티에 각각 3 ,4가 할당됨
- (2) - 프로토타입 객체에 할당한 메서드는 인스턴스가 마치 자신의 것처럼 호출할 수 있음
- (3) - rect1인스턴스에서 isRectangle이라는 메서드에 접근하고자 했지만
rect1
에도 ,rect1.__proto__
에도 없으며,rect1.__proto__.__proto__
에도 없음 그래서 undefined 에러 발생. 이렇게 인스턴스에서 직접 접근할 수 없는 메서드를 스태틱 메서드라고 함 - (4) - 생성자 함수를 this로 해야만 호출할 수 있음
구체적인 인스턴스가 사용할 메서드를 정의한 ‘틀’역할을 담당하는 목적을 가질 때에는 클래스는 추상적인 개념이지만 ,
클래스 자체를 this로 해서 직접 접근해야만 하는 스태틱 메서드를 호출할 때의 클래스는 그 자체가 하나의 개체로서 취급됨
03. 클래스 상속
- 클래스 상속을 객체지향에서 가장 중요한 요소 중 하나
클래스에 있는 값이 인스턴스의 동작에 영향을 주는 경우
var Grade = function(){
var args = Array.prototype.slice.call(arguments);
for (var i =0; i <args.lengthl i++){
this[i] = args[i];
}
this.length = args.length;
};
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);
colsole.log(g); //Grade{ 0:70 , 1:80 , 2:90 , legth :1};
g.__proto__
, 즉 Grade.prototype이 빈 배열을 가리키고 있어서 g.push(70)을 하면 0번 인덱스에 70이 들어가고 length가 1이 되는거임- push 명령에 의해 자바스크립트 엔진이 g.length를 읽고자 하는데 g.length가 없으니까 프로토타입 체이닝을 타고
g.__proto__.length
를 읽어오는 것임. 빈 배열을 가리키고 있으니까 length는 1이 되는거고
만약 Grade.prototype에 요소를 포함하는 배열을 매칭시켰다면?
Grade.prototype = ['a' , 'b' , 'c' , 'd'];
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:100 , 1 :80 , 2:90 ,__ 4:70 ,length :5}
- g.length를 지우고 g.push(70) 를 하면 g.length 가 없으니까
g.__proto__.length
를 찾고, 값이 4이므로 인덱스 4에 70을 넣고 , 다시 g.length에 5를 부여하는 거임
이처럼 클래스에 있는 값이 인스턴스의 동작에 영향을 줄 수 있다는 것 자체가 이미 클래스의 추상성을 해침
사용자가 정의한 두 클래스 사이에서의 상속관계의 경우
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 Squeare = function(width){
this.width = width;
};
Square.prototype.getArea = function(){
return this.width * this.width;
};
var sq = new Square(5);
console.log(sq.getArea()); //25
// Square 클래스 변형
var Sqaure = function(width){
this.width = width;
this.height = width;
};
Square.prototype.getArea = function(){
return this.width * this.height;
};
// Rectangle를 상속하는 Square 클래스
var Square = function(width){
Rectangle.call(this , width ,width);
};
Square.prototype = new Rectangle();
var rect2 = new sq.constructor(2,3);
console.log(rect2); // Rectangle { width:2 , height:3}
- Square를 Rectangle 의 하위 클래스로 삼을 수 있을 것 같음
- getArea라는 메서드는 동일한 동작을 하므로 상위 클래스에서만 정의하고 , 하위클래스에서는 해당 메서드를 상속하면서 height 대신 width를 넣어주면 일단 원하는대로 동작하긴 함
- 하지만 위 코드 가지고 완벽한 클래스 체계가 구축되었다고 볼 수는 없음 , 구조적으로 안전성이 떨어짐
클래스가 구체적인 데이터를 지니지 않게 하는 방법
- 일단 만들고 나서 프로퍼티들을 일일이 지우고 더는 새로운 프로퍼티를 추가할 수 없게 하는 것
'4-1기 스터디 > Javascript-Typescript Deep Dive' 카테고리의 다른 글
[JS Deep Dive 스터디] 6주차 - 프로토타입 (0) | 2023.01.25 |
---|---|
[JS Deep Dive 스터디] 5주차 - 클로저 (0) | 2023.01.25 |
[JS Deep Dive 스터디] 4주차 - 콜백 함수 (1) | 2023.01.25 |
[JS Deep Dive 스터디] 3주차 - this (0) | 2023.01.25 |
[JS Deep Dive 스터디] 2주차 - 실행 컨텍스트 (0) | 2022.11.06 |
댓글