[모던 Js Deep Dive] 25장. 클래스

Jinny·2023년 9월 25일

25.1 클래스 정의

클래스는 class 키워드를 사용하여 정의한다.
클래스 이름은 생성자 함수와 마찬가지로 파스칼 케이스를
사용하는 것이 일반적이다.

class Person {
	
    //생성자
    constructor(name) {
    	//인스턴스 생성 및 초기화
        this.name = name;
    }
    //프로토타입 메서드
    sayHi() {
    	console.log(`Hi! My name is ${this.name}`);
     }
     
    //정적 메서드
    static sayHello() {
    	console.log('Hello!');
    }
 }
 
 const person1 = new Person('Lee');
 person1.sayHi();  // Hi! My name is Lee
 Person.sayHello();  // Hello!
  • 클래스 몸체에서 costructor(생성자), 프로토타입 메서드, 정적 메서드로 3가지가 있다.
  • 다른 방법으로 constructor 안에 this.sayHi = function() {}으로 함수를 정의할 수도 있다.

25.2 인스턴스 생성

클래스는 생성자 함수이며 new 연산자와 함께 호출되어 인스턴스를 생성한다.

class Person {}
const person1 = new Person();
  • 클래스는 인스턴스를 생성하는 것이 유일한 존재 이유이므로
    반드시 new 연산자와 함께 호출해야 한다.

  • 클래스를 가리키는 식별자(Person)을 사용해 인스턴스를 생성해야 한다.

25.3 메서드

25.3.1 constructor

constructor는 인스턴스를 생성하고 초기화하기 위한 특수한 메서드이다.
➡️ 객체를 여러 개 복사하고 싶을 때 사용함

class Person {
  constructor(name) {
    this.name = "Jeong";
    this.address = "Seoul";
  }
}

const person1 = new Person();
console.log(person1); // Person {name: "Jeong", address: "Seoul"}
  • 여기서 this는 새로 생성되는 객체를 의미하며 그 객체에 값을 부여할 수 있다.
  • 즉, 인스턴스를 생성하면 Person이 가지고 있는 name 프로퍼티가 추가된다.
  • constructor는 클래스 내에 최대 한 개만 존재할 수 있으며,
    2개 이상 constructor를 포함하면 문법 에러가 발생한다.
class Person {}
const person1 = new Person();
console.log(person1); //{} 
  • constructor는 생략이 가능하며 생략 시 빈 constructor가 암묵적으로 정의된다. constructor를 생략한 클래스는 빈 constructor에 의해
    빈 객체를 정의한다.

25.3.2 프로토타입 메서드

  1. 생성자 함수를 통해 인스턴스를 생성하는 경우
    다음과 같이 명시적으로 프로토타입에 메서드를 추가해야 한다.
//생성자 함수
function Person(name) {
  	this.name = name;
}

//프로토타입 메서드
Person.prototype.sayHi = function () {
  	console.log(`Hi! My name is ${this.name}`);
};

const person1 = new Person('Lee');
console.log(person1);
person1.sayHi(); // Hi! My name is Lee
  • Prototype은 유전자로 프로토타입에 값을 추가하면 모든 자식들이
    물려받을 수 있다.
  • person1에는 sayHi 속성이 없지만 부모 유전자가 등록해둬서
    자식들도 이 함수를 실행할 수 있다.
  1. 클래스 몸체에서 정의한 메서드는 생성자 함수와 다르게 prototype 키워드에 메서드를 추가하지 않아도 기본적으로 프로토타입 메서드가 된다.
class Person {
  	constructor(name) {
  		this.name = name;
    } 
  
  //프로토타입 메서드
	sayHi () {
  	console.log(`Hi! My name is ${this.name}`);
	}
}

const person2 = new Person('Kim');
person2.sayHi(); // Hi! My name is Kim

25.3.3 정적 메서드

정적(static) 메서드는 인스턴스를 생성하지 않아도 호출할 수 있는 메서드를 뜻한다.

class Person {
  	constructor(name) {
      	this.name = name;
    }
  
  	static SayHi() {
      	console.log('Hi~!');
    }
}

Person.sayHi(); // Hi
  • 클래스에서는 메서드에 static 키워드를 붙이면 정적 메서드가 된다.
  • 정적 메서드는 인스턴스로 호출하지 않고 클래스로 호출한다.

25.3.4 정적 메서드 vs 프로토타입 메서드

  1. 정적 메서드와 프로토타입 메서드는 자신이 속해 있는 프로토타입 체인이 다르다.
  2. 정적 메서드는 클래스로 호출하고 프로토타입 메서드는 인스턴스로 호출한다.
  3. 정적 메서드는 인스턴스 프로퍼티를 참조할 수 없지만 프로토타입 메서드는 인스턴스 프로퍼티를 참조할 수 있다.

25.4 프로퍼티

25.4.1 인스턴스 프로퍼티

인스턴스 프로퍼티는 constructor 내부에서 정의해야 한다.

class Person {
  	constructor(name) {
      //인스턴스 프로퍼티
      this.name = name;
      this.lastName = "Jeong";
    }
}
  • constructor 내부에서 this에 추가한 프로퍼티는 클래스가 생성한 인스턴스의 프로퍼티가 된다.
  • 인스턴스 프로퍼니는 언제나 public하다.

25.4.2 접근자 프로퍼티

접근자 프로퍼티는 자체적으로 값을 갖지 않고 데이터 프로퍼티의 값을 읽거나 저장할 때 사용하는 접근자 함수로 구성된 프로퍼티이다.

const person = {
  	//데이터 프로퍼티
  	firstName : "mujin",
  	lastName : "Kim",
  
  	get fullName() {
      	return  `${this.lastName} ${this.firstName}`;
    },
  
  	set fullName(name) {
      	[this.lastName,this.firstName] = name.split(' ');
    }
};

//setter 함수 호출
person.fullName = "Lee Jieun";
console.log(person); // {firstName: 'Jieun', lastName: 'Lee'}
//getter 함수 호출
console.log(person.fullName); // 'Lee Jieun'
  • 접근자 함수는 getter 함수와 setter 함수로 구성되어 있다.

  • getter는 인스턴스 프로퍼티 값을 조작할 때 사용되며, setter은 인스턴스 프로퍼티에 값을 할당할 때 사용한다.

  • getter/setter는 메서드 이름 앞에 get/set 키워드를 사용해 정의한다.

  • getter는 반드시 무언가를 return해야 하고 setter는 무언가를 프로퍼티에 할당해야 하므로 반드시 매개변수가 있어야 한다.

25.5 상속에 의한 클래스 확장

상속에 의한 클래스 확장아느 기존 클래스를 상속받아 새로운 클래스를 확장하여 정의하는 것이다.

25.5.1 extends 키워드

extends 키워드를 사용해 상속받을 클래스를 정의한다.
클래스를 복사한다고 생각하면 쉽다.

class Father{
  constructor(name){
    this.lastName = 'Kim';
    this.name = name;
  }
}

//생성자 함수를 상속받는 서브클래스
class Son extends Father {
		constructor(){
				super(name);
				this.age = 25;
		}
}

const son1 = new Son('민수');
console.log(son1);    //{age: 25,lastName: "Kim",name: "민수"}
  • extends 키워드를 통해 생성된 class는 바로 this를 쓸 수 없어
    super 키워드를 먼저 써야 this를 사용할 수 있다.
  • 이때 super 키워드는 물려받는 클래스의 constructor를 호출한다.
  • 아버지 클래스에서 name 이라는 파라미터를 입력했으므로
    아들 클래스에서도 동일하게 파라미터를 명시해줘야
    아버지가 가진 속성들을 정확히 상속받을 수 있다.

25.5.2 super 키워드

super 키워드는 물려받는 클래스(= 수퍼클래스)의 constructor를 호출한다.

class Base {
  	constructor(a,b) {
      	this.a = a;
      	this.b = b;
    }
}

//서브 클래스
class Derived extends Base {
  	constructor(a,b,c) {
      	super(a,b);
      	this.c = c;
    }
}

const derived1 = new Derived(1,2,3);
console.log(derived1);
  • Derived 클래스를 호출하면 인수 1,2,3은 Derived 크래스의 constructor에 전달되고 super 호출을 통해 Base 클래스의 constructor에 일부(1,2)가 전달된다.

❗❗주의사항

  1. 서브클래스의 constructor에서 super를 호출하기 전에는 this를 참조할 수 없다.

  2. 서브클래스에서 constructor를 생략하지 않는 경우 서브클래스의 constructor에서 반드시 super를 호출해야 한다.

  3. super는 반드시 서브클래스의 constructor에서만 호출한다.
    서브클래스가 아닌 클래스의 constructor나 함수에서 호출하면 에러가 발생한다.

☑️ super 참조
메서드 내에서 super를 참조하면 수퍼클래스의 메서드를 호출할 수 있다.

class Base {
  	constructor(a,b) {
      	this.a = a;
      	this.b = b;
    }
  	printParameter(){
      console.log(this.a,this.b);
    }
}

class Derived extends Base {
  	constructor(a,b,c) {
      	super(a,b);
      	this.c = c;
    }
}

const Derived1 = new Derived(1,2,3);
console.log(Derived1); // Derived {a: 1, b: 2, c: 3}
Derived1.printParameter(); // 1 2
  • Base 클래스 함수를 상속받으면 역시 서브클래스(Derived1)에서 실행할 수 있다.
  • 우선 Derived1 클래스에서 해당 함수가 있는지 여부를 판단한다. 만약 없으면 그 다음 부모 프로타입에 printParameter 함수가 있는 것을 확인하고 실행할 수 있는 것이다.

0개의 댓글