일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- next13
- 리액트쿼리
- NPM
- 알고리즘문제풀이
- 자바스크립트 연결리스트
- leetcode
- stack문제
- Baekjoon
- 프론트엔드
- 자바스크립트 문제
- Next.js13
- JS
- lodash
- HTML
- 자바스크립트 문제풀이
- 자바스크립트 문제 풀이
- 타입스크립트
- Next
- JavaScript
- react
- 자바스크립트
- 자바스크립트 알고리즘 문제
- CSS
- 리액트
- 제로베이스
- 프로그래머스
- til
- leetcode문제풀이
- 자바스크립트코딩테스트
- 자바스크립트 알고리즘
- Today
- Total
코드노트
자바스크립트 클래스 정리 본문
클래스
- 클래스는 생성자 함수와 유사하게 동작하지만 몇 가지의 차이를 가지고 있다.
- 클래스는 class 키워드를 사용하여 정의
// 클래스 선언문
class Person{}
// 익명 클래스 표현식
const Person = class {};
// 기명 클래스 표현식
const Person = class MyClass {};
- 클래스는 표현식으로 정의할 수 있다.
* 무명의 리터럴로 생성할 수 있고 런타임에 생성 가능
* 변수, 자료구조(객체, 배열) 저장 가능
* 함수의 매개변수에 전달 가능
* 함수의 반환값으로 사용
1. 클래스를 new 연산자 없이 호출하면 에러가 발생
- 생성자 함수를 new 연산자 없이 호출하면 일반 함수로 호출
2. 클래스는 상속을 지원하는 extends와 super 키워드를 제공
- 생성자 함수는 extends와 super키워드를 지원하지 않는다.
3. 클래스는 호이스팅이 발생하지 않는 것처럼 동작
- 함수 선언문으로 정의된 생성자 함수는 함수 호이스팅 발생
- 함수 표현식으로 정의한 생성자 함수는 변수 호이스팅이 발생
4. 클래스 내의 모든 코드에는 암묵적으로 strict mode가 지정되어 실행되며 strict mode를 해제할 수 없다.
- 생성자 함수는 암묵적으로 strict mode가 지정되지 않는다.
5. 클래스의 constructor, 프로토타입 메서드, 정적 메서드는 모두 프로퍼티 어트리뷰트 [[ Enumerable ]]의 값이 false
선언 방법
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
클래스 사용 예제
// 클래스 선언문
class Person {
// 생성자
constructor(name) {
//인스턴스 생성 및 초기화
this.name = name;
}
// 프로토타입 메서드
sayHi() {
console.log(`Hi! My name is ${this.name}`);
}
// 정적 메서드
static sayHello() {
console.log("Hello!");
}
}
// 인스턴스 생성
const me = new Person("Lee");
// 인스턴스의 프로퍼티 참조
console.log(me.name) // Lee
// 프로토타입 메서드 호출
me.sayHi(); // Hi! My name is Lee
// 정적 메서드 호출
Person.sayHello(); // Hello!
- 클래스 body에 정의할 수 있는 메서드는 constructor(생성자), 프로토타입 메서드, 정적 메서드 세가지이다.
클래스 호이스팅
*변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것을 의미
클래스 선언문으로 정의한 클래스는 함수 선언문과 같이 소스코드 평과 과정, 즉 런타임 이전에 먼저 평가 되어 함수 객체를 생성한다.
클래스 선언문은 마치 호이스팅이 발생하지 않는 것처럼 보이지만 그렇지 않다.
- 단 클래스는 클래스 정의 이전에 참조할 수 없다.
* var, let, const, function, function*, class 키워드를 사용하여 선언된 모든 식별자는 호이스팅 된다.
모든 선언문은 런타임 이전에 먼저 실행되기 때문인다.
ex ) 생성자 함수를 class로
// 생성자 함수
function Person(name) {
this.name = name;
}
// 프로토타입 메서드
Person.prototype.getName = function () {
return this.name + "입니다";
}
// 정적 메서드
Person.sayHello = function() {
console.log("Hello!")
}
// Classes
class Person {
constructor(name) {
this.name = name;
}
// 프로토타입 메서드
getName() {
return this.name + "입니다";
}
static sayHello() {
console.log("Hello!")
}
}
- 생성자함수보다 더 간결하게 사용할 수 있다. 그 외에도 다른 메서드들을 사용할 수 있다.
- 리엑트, 뷰 등에서도 사용이 가능하기 때문에 생성자함수를 꼭 사용할 필요는 없다.
- 클래스는 new 연산자 없이 호출하면 에러가 발생 / 생성자 함수는 일반 함수로 호출
인스턴스 생성
- 클래스는 생성자 함수이며 new 연산자와 함께 호출되어 인스턴스를 생성한다.
class Person {}
// 인스턴스 생성
const me = new Person();
console.log(me); // Person {}
- 클래스는 인스턴스를 생성하는 것이 유일한 존재 이유이므로 반드시 new 연산자와 함께 호출해야 한다.
const Person = class MyClass {};
const me = new Person();
console.log(me); // MyClass {}
console.log(MyClass); // ReferenceError: MyClass is not defined
const you = new MyClass();
console.log(you); // ReferenceError: MyClass is not defined
- 기명 함수 표현식과 마찬가지로 클래스 표현식에서 사용한 클래스 이름은 외부 코드에서 접근 불가능하다.
메서드
- 클래스 body에는 0개 이상의 메서드만 선언할 수 있다. constructor(생성자), 프로토타입 메서드, 정적 메서드 세가지가 있다.
constructor
- 인스턴스를 생성하고 초기화하기 위한 특수한 메서드
- 이름을 변경할 수 없다.
class Person {
// 생성자
constructor(name) {
// 인스턴스 생성 및 초기화
this.name = name;
}
}
- 클래스 내에 1개만 존재할 수 있다. 2개 이상 존재하면 문법 에러가 발생한다.
- 클래스는 인스턴스를 생성하기 위한 생성자 함수이다.
- constructor는 메서드로 해석되는 것이 아니라 클래스가 생성한 객체 코드의 일부가 된다.
- 생략할 수 있지만 생략을 하게 되면 빈 constructor가 빈 객체로 암묵적으로 정의된다.( 빈 객체를 생성 )
- 프로퍼티가 추가되어 초기화된 인스턴스를 생성하려면 constructor 내부에서 this에 인스턴스 프로퍼티를 추가
- return을 사용할 수 있지만 클래스의 기본 동작을 훼손하기 때문에 내부에서 return문을 반드시 생략해야한다.
프로토타입 메서드
// 생성자 함수
function Person(name) {
this.name = name;
}
// 프로토타입 메서드
Person.prototype.sayHi = function() {
console.log("Hi! My name is " + this.name)
};
const me = new Person("lee");
me.sayHi(); // Hi! My name is lee
---------------------------------------
// 클래스
class Person {
// 생성자
constructor(name) {
// 인스턴스 생성 및 초기화
this.name = name;
}
// 프로토타입 메서드
sayHi() {
console.log("Hi! My name is " + this.name)
}
}
const me = new Person("lee");
me.sayHi(); // Hi! My name is lee
- 생성자 함수를 사용하여 인스턴스를 생성하는 경우 프로토타입 메서드를 생성하기 위해서는 명시적으로 프로토타입에 메서드를 추가
- 클래스 body에서 정의한 메서드는 생성자 함수에 의한 객체 생성방식과는 다르게
클래스의 prototype 프로퍼티에 메서드를 추가하지 않아도 기본적으로 프로토타입 메서드가 된다.
* 클래스 내에 메서드를 정의하게 되면 프로토타입 메서드!
정적 메서드
- 인스턴스를 생성하지 않아도 호출할 수 있는 메서드를 말한다.
// 생성자 함수
function Person(name) {
this.name = name;
}
// 정적 메서드
Person.sayHi = function() {
console.log("Hi!");
}
// 정적 메서드 호출
person.sayHi(); // Hi!
------------------------------------------
// 클래스
class Person {
// 생성자
constructor(name) {
// 인스턴스 생성 및 초기화
this.name = name;
}
// 정적 메서드
static sayHi() {
console.log("Hi!");
}
}
// 정적 메서드 호출
// 정적메서드는 클래스로 호출한다.
// 정적 메서드는 인스턴스 없이도 호출할 수 있다.
person.sayHi(); // Hi!
- 클래스에서는 메서드에 static을 붙이면 정적 메서드가 된다.
- 생성자 함수와 비교해도 코드가 간결한걸 볼 수 있다.
- 정적 메서드는 클래스에 바인딩되기 때문에 인스턴스 프로토타입 체인 상에서 찾을 수 없다.
// 인스턴스 생성
const me = new Person("lee");
me.sayHi(); // TypeError: me.sayHi is not a function
- 정적 메서드는 인스턴스로 호출할 수 없다.
정적 메서드와 프로토타입 메서드의 차이
- 정적 메서드와 프로토타입 메서드는 자신이 속해 있는 프로토타입 체인이 다르다.
- 정적 메서드는 클래스로 호출하고 프로토타입 메서드는 인스턴스로 호출한다.
- 정적 메서드는 인스턴스 프로퍼티를 참조할 수 없지만 프로토타입 메서드는 인스턴스 프로퍼티를 참조할 수 있다.
class Square {
// 정적 메서드
static area(width, height) {
return width * height;
}
}
console.log(Square.area(10, 10)); // 100
-------------------------------------------
class Square {
constructor(width, height) {
this.width = width;
this.height = height;
}
// 프로토타입 메서드
area() {
return this.width * this.height;
}
}
const square = new Square(10, 10);
console.log(Square.area()); // 100
- 위 예제를 보더라도 알 수있다. 정적 메서드는 2개의 인수를 받아 계산한다. 인스턴스 메서드를 참조 하지 않는다.
- 그러나 프로토타입 메서드는 인스턴스 메서드를 참조한다.
* 정적 메서드는 this가 아닌 클래스를 가르킨다.
* 프로토타입 메서드는 this를 사용하여 인스턴스 프로퍼티를 참조한다.
-> this를 사용하지 않더라도 프로토타입메서드를 정의할 수는 있다. this를 사용하지 않는 메서드는 정적 메서드로 정의하는것이 좋다
클래스 접근자 프로퍼티
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
// 접근자 함수로 구성된 접근자 프로퍼티 getter, setter 함수
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
set fullName(name) {
[this.firstName, this.lastName] = name.split(" ");
}
}
const me = new Person("beomjun", "kwon");
console.log(`${me.firstName} ${me.lastName}`); // beomjun kwon
me.fullName = "tube kwak";
console.log(me.fullName); // tube kwak
console.log(Object.getOwnPropertyDescriptor(Person.prototype, "fullName"));
/*
{
get: [Function: get fullName],
set: [Function: set fullName],
enumerable: false,
configurable: true
}
*/
- 접근자 프로퍼티는 자체적으로 값을 갖지 않고 다른 데이터 프로퍼티의 값을 읽거나 저장할 때 사용하는 접근자 함수
- getter는 프로퍼티를 참조하는 형식으로 사용
* 프로퍼티에 접근할 때마다 프로퍼티 값을 조작하거나 별도의 행위가 필요할 때 사용
* 무언가를 취득할 때 사용하므로 반드시 반환
- setter는 프로퍼티처럼 값을 할당하는 형식으로 사용
* 무언가를 프로퍼티에 할당해야 할 때 사용
* 반드시 매개변수가 필요!
클래스 필드 정의 제안
- 클래스 기반 언어에서 클래스가 생성할 인스턴스의 프로퍼티를 가르키는 용어
class Person {
this.name = "";
} // SyntaxError: Unexpected token '.'
- 클래스 몸체에서 클래스 필드를 정의하는 경우 this에 클래스 필드를 바인딩 해서는 안된다.
- this는 클래스의 constructor와 메서드 내에서만 유효
class Person {
name = "code";
constructor() {
console.log(name);
}
}
new Person(); // ReferenceError: name is not defined
class Person {
name;
}
const me = new Person();
console.log(me); // Person { name: undefined }
- 클래스 필드에 초기값을 할당하지 않으면 undefined를 갖는다.
- 인스턴스를 생성할 때 외부의 초기값으로 클래스 필드를 초기화해야 할 필요가 있다면 constructor에서 클래스 필드를 초기화
class Person {
name;
constructor(name) {
this.name = name;
}
}
const me = new Person("code");
console.log(me); // Person { name: 'code' }
class Person {
constructor(name) {
this.name = name;
}
}
const me = new Person("code");
console.log(me); // Person { name: 'code' }
- 클래스 필드를 초기화할 필요가 있다면 어차피 constructor 내부에서 클래스 필드를 참조하여 초기값을 할당
- 클래스가 생성한 인스턴스에 클래스 필드에 해당하는 프로퍼티가 없다면 자동 추가
* name이 없어도 constructor에서 자동 추가 된다.
- 이 외에도 함수는 일급 객체이므로 함수를 클래스 필드에 할당할 수 있다. 메서드를 정의할 수도 있다.
- 필드에 함수는 프로토타임 메서드가 아닌 인스턴스 메서드가 된다.
- 모든 클래스 필드는 인스턴스 프로퍼티가 되기 때문
- 클래스 필드에 함수를 할당하는 것은 권장하지 않는다.
private 필드 정의
class Person {
name = "code";
}
const me = new Person();
console.log(me.name); // code
- 클래스 필드는 기본적으로 public하기 때문에 외부에 그대로 노출 된다.
class Person {
#name = ""; // private 필드 정의
constructor(name) {
this.#name = name;
}
}
const me = new Person("code");
console.log(me.name); // undefined
class Person {
#name = ""; // private 필드 정의
constructor(name) {
this.#name = name;
}
get name() {
return this.#name.trim();
}
}
const me = new Person(" code ");
console.log(me.name); // code
- private 필드 #name은 클래스 외부에서 참조할 수 없다.
- 접근자 프로퍼티로는 접근할 수 있다.
- private 필드는 반드시 클래스 몸체에 정의하자. constructor에 정의하면 에러 발생
상속에 의한 클래스 확장
- 프로토타입 기반의 기본 상속과는 다른 개념
- 기존 클래스를 상속받아 새로운 클래스를 확장하여 정의
class Traveler {
constructor(name, age) {
this.name = name;
this.age = age;
}
eat() {
return "eat";
}
move() {
return "move";
}
}
class Kwak extends Traveler {
ssaul() {
return "ssaul";
}
}
const kwak = new Kwak(1, 5);
console.log(kwak); // Kwak { name: 1, age: 5 }
console.log(kwak instanceof Kwak); // true
console.log(kwak instanceof Traveler); // true
console.log(kwak.eat()); // eat
console.log(kwak.move()); // move
console.log(kwak.ssaul()); // ssaul
- 클래스 상속을 통해서 기존 클래스의 속성들을 그대로 사용하고 자신만의 고유한 속성을 추가하여 사용할 수 있다.
- 코드 재사용 관점에서도 매우 유용한 방법
- extends키워드를 사용한 클래스 확장은 간편하고 직관적
extends
// 수퍼(베이스/부모)클래스
class Traveler {
}
// 서브(파생/자식)클래스
class Kwak extends Traveler {
}
- 수퍼클래스와 서브 클래스는 인스턴스의 프로토타입 체인 뿐만 아니라 클래스 간의 프로토타입 체인도 생성
- 프로토타입 메서드, 정적 메서드 모두 상속이 가능
function Traveler(a) {
this.a = a;
}
class Kwak extends Traveler {}
const tube = new Kwak(1);
console.log(tube); // Kwak {a:1}
- extends 키워드는 클래스 뿐만 아니라 생성자 함수를 상속받아 클래스 확장도 가능
- 단, extends 키워드 앞에는 반드시 클래스가 있어야 한다.
function Kwak() {}
class Bottle {}
let condition = true;
// 조건에 따라서 동적으로 상속 대상을 결정할 수 있다.
class Tube extends (condition ? Kwak : Bottle) {}
const traveler = new Tube();
console.log(traveler);
console.log(traveler instanceof Kwak);
console.log(traveler instanceof Bottle);
- extends 키워드 다음에는 클래스뿐만 아니라 [[Construct]] 내부 메서드를 갖는 함수 객체로 평가될 수 있는 모든 표현식을 사용할 수 있다.
- 조건문을 통해서 상속 대상을 결정하는 것을 볼 수있다.
서브클래스의 constructor
- constructor를 생략하게 되면 비어있는 constructor가 암묵적으로 정의
class Bottle {}
class Tube extends Bottle {}
const traveler = new Tube();
console.log(traveler);
----------------------------------------
class Bottle {
constructor() {}
}
class Tube extends Bottle {
constructor(...args) {
super(...args);
}
}
const traveler = new Tube();
console.log(traveler);
- 첫번째 예제를 보면 알 수 있듯이 constructor를 생략하게 되면 두번째 예제처럼 암묵적으로 정의가 되어 실행된다.
- 프로퍼티를 소유하는 인스턴스를 생성하려면 constructor내부에서 인스턴스에 프로퍼티를 추가해야 한다.
'Code note > 자바스크립트' 카테고리의 다른 글
자바스크립트 클래스 super 키워드 이해하기 (0) | 2023.01.03 |
---|---|
스크롤 상단 고정 네비게이션 (0) | 2022.12.31 |
자바스크립트 실행 컨텍스트 Execution context 정리 (0) | 2022.12.22 |
자바스크립트 this 이해하기 (1) | 2022.12.21 |
프로토타입 기반의 객체지향 언어, 자바스크립트 프로토타입 이해 (0) | 2022.12.19 |