-
Section 5. 자바스크립트 클래스자바스크립트 2023. 3. 29. 22:48
I. 클래스 class를 사용하여 인스턴스 만들기
클래스를 사용해서 인스턴스를 만드는 것은 다음과 같다.
class KangChicken { constructor (name, no) { this.name = name; this.no = no; } introduce () { // 💡 메서드 return `안녕하세요, ${this.no}호 ${this.name}점입니다!`; } } const chain1 = new KangChicken('판교', 3); const chain2 = new KangChicken('강남', 17); const chain3 = new KangChicken('제주', 24); console.log(chain1, chain1.introduce()); //KangChicken {name: '판교', no: 3} '안녕하세요, 3호 판교점입니다!' console.log(chain2, chain2.introduce()); //KangChicken {name: '강남', no: 17} '안녕하세요, 17호 강남점입니다!' console.log(chain3, chain3.introduce()); //KangChicken {name: '제주', no: 24} '안녕하세요, 24호 제주점입니다!'
사용법은 생성자 함수와 다를것이 없다.
하지만 완전히 같은 기능을 하는 것은 아니다.
클래스와 생성자 함수의 동작의 차이를 가볍게 살펴보자면 다음과 같다.
// 차이 1. 클래스는 호이스팅되지 않음 (정확히는 되지만...) const chain1 = new KangChicken('판교', 3); //undefined class KangChicken { constructor (name, no) { this.name = name; this.no = no; } introduce () { return `안녕하세요, ${this.no}호 ${this.name}점입니다!`; } } // 차이 2. 클래스는 new 없이 사용하면 오류 // (생성자 함수는 오류 없이 undefined 반환) const chain2 = KangChicken('강남', 17);
예시처럼 클래스는 호이스팅이 되지 않고(undefined를 반환한다) 그리고 클래스는 new 없이 사용하면 오류가 난다.
II. constructor 메서드
class Person { constructor (name, age, married = false) { this.name = name; this.age = age; this.married = married; } } const person1 = new Person('박영희', 30, true); const person2 = new Person('오동수', 18); console.log(person1, person2); //Person {name: '박영희', age: 30, married: true} //Person {name: '오동수', age: 18, married: false} // 인스턴스 초기화가 필요없는 클래스 class Empty {} console.log(new Empty()); //Empty {}
person2 객체에 married인자를 주지 않았는데 자동적으로 false값이 넣어졌다.
또 class Empty 같은 경우 초기화가 필요가 없다. 따라서 이런 경우 그냥 constructor없이 선언해도 된다는 점을 알고 넘어가자.
정리하자면 다음과 같다.
- constructor 매서드는 인스턴스 생성시 인자를 받아 프로퍼티를 초기화한다.
- 클래스에 하나만 있을 수 있음. 초과시에는 오류 발생한다.
- 다른 메서드 이름을 쓸 수 없다.
- 기본값 사용 가능하다.
- 필요없을 (인자가 없을 때 등)시 생략 가능하다.
- 값을 반환하지 말 것! 왜냐하면 생성자 함수처럼 암묵적으로 this 반환하기 때문이다.
III. 클래스의 메서드
클래스의 메서드는 다음과 같다.
class Dog { bark () { return '멍멍'; } } const badugi = new Dog(); console.log(badugi, badugi.bark());
console.log(badugi); 를 했을때 객체 badugi에는 bark()라는 함수가 프로토타입 안에 있는 것을 확인할 수 있다.
다음은 생성자 함수와 비교를 해보자
function Dog2 () { this.bark = function () { return '멍멍'; } } const badugi = new Dog2(); console.log(badugi, badugi.bark());
차이가 보이는가? 생성자 함수로 생성한 인스턴스 badugi는 bark()를 인스턴스 자체에서 가지고 있는 것을 확인할 수 있다.
프로토타입을 통해 가지게 되는 속성이나 메서드는 인스턴스가 사용하긴 하지만 직접적으로 사용하는 것이 아니라 생성자 함수를 통해서 사용하게 되는 것이다.
반면에 인스턴스가 해당 기능을 직접적으로 가지고 있으면 인스턴스 자체에서 직접적으로 사용하는 것이라고 생각하면 된다.
IV. 필드 field
// 필드값이 지정되어 있으므로 constructor 메서드 필요없음 class Slime { hp = 50; op = 4; attack (enemy) { enemy.hp -= this.op; this.hp += this.op/4; } }
클래스의 인스턴스가 기본적으로 가지고 있을 메서드를 정할때 this를 통해서 일일히 설정을 해 줘야 했었는데 이제는 해당 클래스에 들어갈 기본값이 있으면 예시에서의 hp나 op 처럼 써주면 된다.
const slime1 = new Slime(); const slime2 = new Slime(); console.log(slime1, slime2); //Slime {hp: 50, op: 4} Slime {hp: 50, op: 4}
클래스 Slime을 이용해서 프로토타입에 attack 기능이 있는 인스턴스를 2개 만들었다.
slime1.attack(slime2); console.log(slime1, slime2); //Slime {hp: 51, op: 4} Slime {hp: 46, op: 4}
slime1이 slime2를 공격했다. 그리고 다시 2개의 인스턴스를 출력해 봤을때 slime1의 attack 기능에 의해서 slime2의 hp가 감소한 것을 볼 수 있다.
또 예시를 보면 다음과 같다.
class KangChicken { no = 0; menu = { '후라이드': 10000, '양념치킨': 12000 }; constructor (name, no) { this.name = name; if (no) this.no = no; } introduce () { return `안녕하세요, ${this.no}호 ${this.name}점입니다!`; } order (name) { return `${this.menu[name]}원입니다.` } }
클래스 KangChicken에 필드로 no와 menu를 설정한 모습을 볼 수 있다.
생성자 함수에서는 name과 no를 받아서 this를 통해 설정한 모습이다.
그리고 order기능이 있는데 name을 받아 return하는 기능이 되겠다.
const chain0 = new KangChicken('(미정)'); console.log(chain0, chain0.introduce()); //KangChicken {no: 0, menu: {…}, name: '(미정)'} '안녕하세요, 0호 (미정)점입니다!' const chain1 = new KangChicken('판교', 3); console.log(chain1, chain1.introduce()); //KangChicken {no: 3, menu: {…}, name: '판교'} '안녕하세요, 3호 판교점입니다!'
chain0인스턴스를 생성했다. 이 인스턴스는 menu를 후라이드 : 10000, 양념치킨 : 12000의 프로퍼티를 가지고 있고
no는 0, name은 '미정'이며 introduce기능과 order기능도 가지고 있다.
chain1도 마찬가지고 인자로 3을 할당했으니 no가 0이 아니라 3을 가지게 되고 name은 '판교'를 가지게 된다.
여기서 chain1의 메뉴중 양념치킨의 가격을 올려보면 다음과 같다.
chain1.menu['양념치킨'] = 13000; console.log(chain0.order('양념치킨'), chain1.order('양념치킨')); //12000원입니다. 13000원입니다.
chain1의 양념치킨의 가격만 변경이된 것을 볼 수 있다.
프로퍼티로 넣어준 no나 menu는 그 인스턴스에 각각 개별적으로 초기화된 값이라고 알 수 있다. chain0과 chain1의 프로퍼티 값은 초깃값만 같을뿐 그것들이 갖고 있는 원시값이던 참조값이던 서로 다른 값을 가지고 있는 것을 알 수 있다.
V. 정적 static 필드와 메서드
class KangChicken { // 정적 변수와 메서드 static brand = '깅치킨'; static contact () { return `${this.brand}입니다. 무엇을 도와드릴까요?`; } constructor (name, no) { this.name = name; this.no = no; } introduce () { return `안녕하세요, ${this.no}호 ${this.name}점입니다!`; } } console.log(KangChicken); console.log(KangChicken.contact()); //강치킨입니다. 무엇을 도와드릴까요?
클래스에서는 static을 앞에 써주면 정적 변수 또는 메서드로 선언할 수 있다. 이렇게 해서 KangChicken의 인스턴스가 아닌 KangChicken 클래스 자체에 contact라는 기능을 수행할 수가 있다.
정적 필드와 일반필드의 다른 점은 다음과 같다.
메모리에서 KangChicken처럼 클래스나 생성자 함수가 차지하는 자리가 있고 그것들이 생성된 인스턴스들이 차지하는 공간들이 있다.
이 인스턴스들은 각각 만들어지는 대로 따로따로 공간을 차지하게 된다.
반면에 생성자 함수나 클래에 정적으로 만들어둔 필드들은 그 공간 하나만 차지하게 된다. 즉, KangChicken의 브랜드는 한 공간만 가지고 있게 되는 것이다.
*정적 메서드에서는 정적 필드만 사용 가능하다는 점을 기억하자.
추가적으로 하나만 더 참고하고 마무리 하겠다.
클래스는 함수이다.
class Dog { bark () { return '멍멍'; } } console.log(typeof Dog); //function
클래스의 타입이 함수로 출력되는 것을 볼 수 있다. 자바스크립트 내부적으로는 함수를 사용해서 클래스가 구현이 된 것이다.
그렇다면 일급객체에서 배웠듯이 함수는 다른 곳에 할당이 될 수 있다고 했다.
const 개 = Dog; // 할당될 수 있는 일급 객체 const 바둑이 = new 개(); console.log(바둑이); // 💡 콘솔에 나타난 타입 확인
이처럼 Dog 클래스를 개에 할당을 해서 바둑이라는 인스턴스를 만들고 출력했을때 결과는 위와 같다. 바둑이 인스턴스는 bark를 가지고 있는 것을 확인할 수 있다.
'자바스크립트' 카테고리의 다른 글
Section 5. 자바스크립트 상속 (0) 2023.03.30 Section 5. 자바스크립트 접근자 프로퍼티와 은닉 (0) 2023.03.30 Section 5. 자바스크립트 생성자 함수 (0) 2023.03.29 Section 5. 자바스크립트 객체의 기본 사용법들 (0) 2023.03.27 Section 4. 자바스크립트 함수 더 알아보기 (0) 2023.03.26