프로토타입 기반 객체 지향
- 자바스크립트는 엄밀히 따지면 클래스와 인스턴스를 기반으로 한 전통적인(?) 방식의 객체 지향 언어는 아니다.
다만 프로토타입이라는 것을 활용해 마치 클래스를 사용하는 것 처럼 보일 뿐이다. - 프로토 타입을 간단하게 정의하면, 모델의 청사진/ 설계도를 만들 때 쓰는 원형 객체이다. 우리가 생성하는 모든 자바스크립트 객체가 이 프로토타입과 연관되어 있다.
- 자바스크립트에서는 이 프로토타입 객체를 복제하여 마치 클래스로부터 인스턴스가 생성되는 것 처럼 활용한다. 말하자면 프로토타입 객체는 부모 객체가 되는 것이다.
- 프로토타입이 가진 메서드를 모든 인스턴스 객체가 접근하여 사용할 수 있는데. 이를 프로토타입 상속이라고 하며, 클래스 상속과는 약간 다른 개념이다. 일반 클래스에서 인스턴스 객체에게 클래스가 가진 프로퍼티를 상속시켜준다면, 자자바스크립트에서는 각 인스턴스 객체가 자신들이 사용해야하는 메서드/행위(프로퍼티)를 프로토타입에게 위임하는 형태이다.
- MDN에서 내장 메서드를 검색하다 보면, Array.prototype.<method> 이런 형태를 많이 볼 수 있을 것이다. 이 Array.prototype이라는 것이 모든 배열 객체의 프로토타입(원형 객체) 이다. 이 프로토타입 배열이 배열에서 쓸 수 있는 모든 내장 배열 메서드를 포함하고 있기 때문에, 우리는 map 같은 메서드를 호출할 때 이 prototype에게 위임하여 실행한다고 볼 수 있다.
자바스크립트에서 클래스-인스턴스 생성하기
- 엄밀히 말하면 클래스 - 인스턴스는 아니지만 편의상 이렇게 표기를 했다.
- JS에서 클래스를 통해 인스턴스 객체를 생성하는 방법에는 크게 3 가지 방법이 있다. 그 방법은 아래와 같다.
생성자 함수 방식 (ES5)
- 일반 함수로 부터 객체를 만들기 위한 문법? 기술이다.
- 자바스크립트 내장 객체인 Array, Maps, Sets가 구현되는 방식이 이 생성자 함수 방식이다.
- 자바스크립트 초창기부터 객체 지향 방식을 도입하기 위해서 사용한 방식이다.
ES6 클래스 문법
- ES6부터 자바스크립트에 클래스 문법이 도입되었다. 생성자 함수 대신 많이 쓰는 현대적이고 대세인 방식이다.
- class 라는 키워드가 있다고 해서 실제 클래스를 사용하는 것은 아니다. 동작 방식은 ES5 방식인 생성자 함수 방식과 동일하지만, 문법적으로 더 편리하게 쓸 수 있도록 도와주는.. Syntax Sugar 문법..설탕? 이다.
Object.create()
- 하나의 객체를 prototype 객체와 연결시키는 가장 쉽고 직관적인 방법이다. 위 두 방법과는 결이 약간 다르다.
생성자 함수 방식으로 클래스 - 인스턴스 생성하기
- OOP 에서 클래스의 이름 (생성자 함수의 함수명)은 늘 대문자로 시작한다.
new Array(), new Map(), new Set() 이렇게 내장 객체를 생성할 때의 방식을 떠올리면 이해가 쉽다. 이들도 이러한 컨벤션을 따르기 때문에 앞글자가 대문자인 것. - 화살표 함수는 this 키워드를 가지지 않기 때문에, 생성자 함수는 함수 선언 혹은 함수 표현식만을 사용한다.
function CounerES5() {
// 인스턴스의 프로퍼티
this.value = 0;
// 인스턴스의 메서드 : 이런 방식으로 작성하는 것은 권장되지 않음.
// 수천개의 인스턴스가 생겼을 때 각 인스턴스가 메서드를 가지고 다닐 것이기 때문에 효율적이지 않다.
// 프로토타입 상속을 이용해 생성하는 것이 권장됨.
this.increase = function () {
this.value++;
};
this.decrease = function () {
this.value--;
};
CounerES5.prototype.getValue = function () {
return this.value;
};
}
// new 키워드로 인스턴스 생성
let counter2 = new CounerES5();
counter2.decrease();
counter2.decrease();
let res2 = counter2.getValue();
- new 연산자로 CounterES5 생성자 함수를 호출할 때 벌어지는 일은 아래와 같다.
- 빈 객체를 생성한다. {} 이 빈 객체가 생성자 함수가 생성한 객체 인스턴스이다.
- 함수가 호출되고, this = {} 빈 객체가 this에 바인딩된다.
- {}가 프로토타입 객체에 연결된다. (this가 바인딩 되어 있는 빈 객체 {} 인스턴스가 초기화된다.)
- 생성자 함수가 암묵적으로 this를 리턴한다.
- +) 생성자 함수 내에서 리턴문을 생략해야만 암묵적으로 this가 반환된다. 다른 객체를 리턴하고 있을 때는 그 객체가 리턴되기 때문에 반드시 리턴문 없이 작성해야한다! 단 명시적으로 원시값을 리턴할 때 이 원시값은 무시됨.
- 여기서 잠시 용어를 정리하고 넘어가자면!
- 프로토타입 prototype : 모델의 청사진을 만들 때 쓰는 원형 객체
- constructor : 인스턴스가 초기화 될 때 실행되는 생성자 함수
- this : 함수가 실행될 때, 해당 스코프마다 생성되는 고유한 실행 컨텍스트(말이 좀 잘 이해가 안간다.) new 키워드로 인스턴스를 생성했을 때 this는 인스턴스 객체를 의미하고, this.value라면 이 인스턴스 객체의 value 프로퍼티를 의미하는 것이다.
ES6 클래스 문법으로 인스턴스 생성하기
- 함수 선언과 클래스 선언의 차이는 호이스팅 발생 여부에 있다. 함수는 호이스팅 현상으로 정의하기 전에 호출할 수 있지만, 클래스는 정의가 된 다음에 호출하도록 되어있다.
- 인스턴스 객체를 생성하고 초기화하기 위한 constructor 메서드 부분과, 인스턴스의 메서드를 설정해주는 부분으로 나눌 수 있다. 참고로 constructor 메서드는 각 클래스당 하나만 가질 수 있다. 여러개 작성하면 문법 오류가 일어난다.
class CounterES6 {
// class로 생성된 객체 즉 인스턴스를 생성하고 초기화하기 위한 메서드.
constructor() {
this.value = 0;
}
// 인스턴스의 메서드는 아래와 같이 정의한다.
increase() {
this.value++;
}
decrease() {
this.value--;
}
getValue() {
return this.value;
}
}
- 이렇게 작성된 클래스를 통해 인스턴스를 생성할 수 있다. 인스턴스를 생성하는 방법은 new 연산자를 붙여 호출하는 ES5 방식과 큰 차이는 없다.
// new 키워드로 인스턴스 생성
let counterEs6 = new CounterES6();
counterEs6.increase();
let res1 = counterEs6.getValue();
- 추가적으로 instanceof 연산자를 사용해 객체가 생성자 함수로 부터 나온 인스턴스인지 확인할 수 있다.
console.log(hyejung instanceof Person); // true
console.log(snadeul instanceof Person); // false
참고자료
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Classes
https://www.udemy.com/course/the-complete-javascript-course
- 코드스테이츠 유어클래스 자료
'JavaScript > JavaScript' 카테고리의 다른 글
[JavaScript] 프로토타입 체인 (0) | 2022.07.25 |
---|---|
[JavaScript] 프로토타입 (0) | 2022.07.22 |
[JavaScript] 객체지향 프로그래밍 : OOP란 무엇인가? (0) | 2022.07.22 |
[JavaScript] 클로저와 커링 초간단 정리 (0) | 2022.07.21 |
[JavaScript] Promise (0) | 2022.07.18 |