
JavaScript는 prototype기반의 객체지향 프로그래밍 언어이지만
ES6부터 추가된 class는 직관적으로 쉽게 코드를 읽을 수 있게 만들어 줄 수 있다.
Prototype
function Me(name) {
this.name = name;
}
Me.prototype.wow = function () {
console.log("WOW!");
};
let person = new Me("Jason");
person.wow(); // WOW!
Class
class Me {
constructor(name){
this.name = name;
}
wow(){
console.log("WOW!");
}
}
let person = new Me("Jason");
person.wow() // WOW!
어떤 생성자 함수를 new 연산자와 함께 호출하면 Constructor에서 정의된 내용을 바탕으로 새로운 인스턴스 생성한다.
인스턴스에는 proto라는 프로퍼티가 자동으로 부여되고 이는 Constructor의 prototype이라는 프로퍼티 참고한다.
//__proto__가 생성자의 prototype 프로퍼티를 참조
const person = new Person('will');
Person.prototype === person.__proto__ //true
new 연산자로 Constructor를 호출하면 인스턴스가 만들어지는데, 이 인스턴스의 생략 가능한 프로퍼티인 proto는 Constructor의 prototype을 참조한다.
이는 생성자 함수의 prototype에 어떤 메소드나 프로퍼티가 있다면 인스턴스에서도 메소드나 프로퍼티에 접근할 수 있다는 뜻이다.
__proto____proto__는 접근하고자 하는 객체의 내부 속성인 prototype을 노출하는 접근자 속성 함수이다.
모든 객체는 __proto__로 통해 자신의 프로토타입에 접근할 수 있다.
그러나 내부 슬롯에 직접적으로는 접근이 불가하다. 그 이유는 프로토타입 체인의 단방향을 지키기 위해서다.
클래스는 ES6부터 추가된 문법으로, 객체 생성자로 구현했던 코드를 깔끔하게 구현할 수 있도록 도와주며 상속도 쉽게 할 수 있다.
객체를 직접 작성하여 정의하고 생성할 수도 있지만 클래스로 만들어주면 여러 객체를 더 쉽게 만들 수 있다.
클래스를 통해 원하는 구조의 객체 틀을 짜놓고 비슷한 모양의 객체를 공장처럼 찍어낼 수 있다.
쉽게 생각해서 클래스 === 붕어빵 기계, 객체 ==== 붕어빵으로 보면 된다.
// constructor()메소드에서 2개의 매개변수 namer과 age로
// Korean 인스턴스의 name, age 프로퍼티에 값을 할당
class Korean {
constructor(name, age) {
this.name = name;
this.age = age;
this.country = 'Korea';
}
addAge(age) {
return this.age + age;
}
}
클래스 내에 정의된 함수를 method라 부름
클래스를 통해 생성된 객체를 인스턴스(instance)라고 부름
클래스도 함수처럼 호출하기 전까지는 코드가 실행되지 않음
⇒ new키워드와 소괄호()를 사용하여 호출
클래스 이름은 대문자로 시작
constructor는 class에서 필요한 기초 정보를 세팅하는 곳
⇒ 객체를 new로 생성할 때 가장먼저 자동으로 호출
클래스에서 this는 본인 객체를 의미
⇒ 클래스 내에서 메소드끼리 소통하기 위해서는 this가 필요
// Korean 클래스를 이용해 kim 객체를 만들면 아래와 같이 instance가 생성
let kim = new Korean("KIMJINYOUNG",24);
{
name: 'KIMJINYOUNG',
age: 24,
country: 'Korea',
addAge: function(age){
return this.age + age;
}
}
class Cat {
constructor(name, age) {
this.name = name;
this.age = age;
}
eat() {
return `${this.name} is eating!`}
meow() {
return 'MEOWWWW'
}
}
class Dog {
constructor(name, age) {
this.name = name;
this.age = age;
}
eat() {
return `${this.name} is eating!`}
bark() {
return 'WOOF'
}
}
두개의 클래스를 보면 조금은 다르지만 복제된 코드들이 많다.
JS에서는 이렇게 복제된 코드를 하나로 관리하여 두 개의 클래스에서 사용할 수 있다.
그렇게 되면 하나로 관리하는 곳은 부모, 해당 코드를 사용하는 곳은 자식이 된다.
이때 사용할 수 있는 키워드가 extend 이다.
class Pet {
constructor(name, age) {
this.name = name;
this.age = age;
}
eat() {
return `${this.name} is eating!`}
}
class Cat extends Pet {
meow() {
return 'MEOWWWW'
}
}
class Dog extends Pet {
bark() {
return 'WOOF'
}
}
이처럼 extend 키워드를 사용하면 자식 클래스인 Dog에서 부모 클래스인 Pet의 값을 사용할 수 있다.
let boksil = new Dog("Boksil",5);
boksil.eat() // Boksil is eating!
Dog 클래스를 보면 eat 메소드와 constructor 메소드가 없는데도 eat 메소드를 사용할 수 있다.
이는 프로토타입에서 eat을 찾지 못하면 프로토타입 체이닝을 통해 부모의 class에서 해당 메소드를 찾는다.
class Dog extends Pet {
bark() {
return 'WOOF'
}
eat() {
return `${this.name} is happy!`}
}
let boksil = new Dog("Boksil",5);
boksil.eat() // Boksil is happy!
Dog에도 eat 메소드를 추가해 준다면 제일 가까이에 있는 Dog의 eat을 출력하게 된다.
부모 클래스의 값을 상속받고, 추가적으로 자식만의 값을 사용하고싶다면 super 키워드를 사용할 수 있다.
class Pet {
constructor(name, age) {
console.log('IN PET CONSTRUCTOR!')
this.name = name;
this.age = age;
}
eat() {
return `${this.name} is eating!`}
}
class Cat extends Pet {
constructor(name, age, livesLeft = 9) {
console.log('IN CAT CONSTRUCTOR!')
super(name, age)
this.livesLeft = livesLeft;
}
meow() {
return 'MEOWWWW'
}
}
const monty = new Cat('monty',9);
// IN CAT CONSTRUCTOR!
// IN PET CONSTRUCTOR!
console.log(monty); // Cat {name: 'monty', age: 9, livesLeft: 9}
monty를 살펴보면 name이 monty이고, age는 9살에 livesLeft는 9로 설정되어 있다.
super 키워드를 사용해 부모 클래스의 constructor (Pet의 생성자)에서 온 기능을 재사용했지만, Cat에 따로 하나를 추가한 것이다.
set : 값을 설정하는 키워드
get : 값을 가져오는 키워드
class Car {
constructor(speed) {
this.speed = speed;
}
get speed() {
return this._speed;
}
set speed(value) {
this._speed = value < 0 ? 0 : value;
}
getSpeed() {
return this.speed;
}
// ...
}
const car1 = new Car(-100);
console.log(car1.getSpeed());
static
class MathUtils {
static APP_NAME = "Math Utils";
static PI = 3.141592653589793;
constructor() {}
static add(a, b) {
return a + b;
}
static minus(a, b) {
return a - b;
}
}
const mathUtils = new MathUtils();
console.log(MathUtils.PI);
console.log(MathUtils.add(10, 20));
console.log(MathUtils.minus(10, 20));
private(#)
class Car {
#speed;
constructor(speed) {
this.#speed = speed;
}
getSpeed() {
return this.#speed;
}
}
const car1 = new Car(100);
console.log(car1.getSpeed());
📍참고