반응형

 내용은 이웅모 님의 모던 "자바스크립트 DeepDive" 책을 보고 정리한 내용입니다. 저작권 보호를 위해 책의 내용은 요약되었습니다.
 

클래스(Class)란?

클래스는 객체 지향 프로그래밍(OOP)을 지원하기 위해 ES6(ES2015)에서 도입된 문법이다. 이전에는 프로토타입 기반의 상속을 사용하여 객체를 생성하고 상속을 구현했지만, 클래스 문법을 도입함으로써 클래스 기반의 상속을 보다 간편하게 사용할 수 있게 되었다.

클래스의 특징

  • 클래스는 무명의 리터럴로 생성이 가능하다. 즉 런타임 중 동적으로 생성이 가능하다.
  • 클래스는 객체로서 변수에 할당할 수 있다. 즉, 클래스를 변수에 담아서 해당 변수를 통해 클래스를 참조할 수 있다.
  • 클래스는 함수의 인자로 전달할 수 있다. 즉, 다른 함수 내에서 클래스를 인자로 받아 처리할 수 있다.
  • 함수 내에서 클래스를 생성하고 해당 클래스를 반환할 수 있다.
  • 클래스 몸체에서 정의할 수 있는 메서드는 constructor(생성자), 프로토타입 메서드, 정적 메서드 세 가지가 있다.

클래스와 생성자 함수

클래스와 생성자는 모두 프로토타입 기반의 인스턴스를 생성하지만 동일하게 동작하지는 않고 생성자 함수보다 엄격하고 생성자 함수보다 많은 기능을 제공한다.

  클래스 생성자 함수
정의 'class' 키워드를 사용하여 정의 일반적인 함수로 정의
객체 생성 'new' 키워드를 사용하여 객체 생성 'new' 키워드를 사용하여 객체 생성
메서드 클래스 내부에서 메서드를 정의 생성자 함수의 프로토타입에 메서드를 추가
상속 'extends' 와 'super' 키워드를 사용하여 상속 지원 '.prototype' 을 사용하여 상속 구현
프로토타입 체인 클래스는 프로토타입 체인을 자동으로 설정 생성장 함수의 프로토타입 체인을 수동으로 설정
호출 클래스는 strict 모드에서만 호출 가능
'new' 연산자 없이 호출 시 에러 발생
'new' 연산자 없이 호출하면 일반적인 함수로 호출 
호이스팅 클래스는 호이스팅이 발생하지 않는 것처럼 동작 생성자함수는 호이스팅이 발생할 수 있음

 

예시) 생정자 함수 정의 방식

var Member = (function(){
    // 생성자
    function Member(name, age){
        this.name = name;
        this.age = age;
    }

    // 프로토 타입 메서드
    Member.prototype.sayHi = fucntion() {
        console.log(`안녕, 나는 ${this.name}이고, ${this.age}살이야!`);
    };

    // 정적 메서드
    Member.sayHello = function() {
      console.log(`안녕하세요, 저는 ${this.name}이고, ${this.age}살입니다.`);
    };
    
}());

 

예시) 클래스 정의 방식

class Member {
    // 생성자
    constructor(name, age) {
      this.name = name;
      this.age = age;
    }
  
    // 프로토 타입 메서드
    sayHi() {
        console.log(`안녕, 나는 ${this.name}이고, ${this.age}살이야!`);
    }

    // 정적 메서드
    static sayHello() {
      console.log(`안녕하세요, 저는 ${this.name}이고, ${this.age}살입니다.`);
    }
}

 

 

클래스의 호이스팅

클래스는 소스코드 평가 과정, 즉 런타임 이전에 먼저 평가되어 함수 객체를 생성하지만 함수와는 다르게 호이스팅되지 않는 것처럼 보인다. let, const 키워드로 선언한 변수처럼 호이스팅은 되나 클래스를 선언하기 전에는 해당 클래스를 참조할 수 없다.

 

예시)

const memer = new Member(); // ReferenceError: Member is not defined

class Member {
  constructor(name) {
    this.name = name;
  }
}

 

 

클래스의 메서드

  • funtion 키워드를 생략한 메서드 축약 표현을 사용한다.
  • 기본적으로 strict mod이다.
  • 클래스의 메서드는 모두 프로퍼티 어트리뷰트 [[Enumerable]]값이 false이므로 for ... in 문이나 Object.keys 메서드 등으로 열거할 수 없다.

1. constructor

  • 객체를 생성하고 초기화하는 데 사용되는 특별한 메서드로 constructor는 이름을 변경할 수 없다.
  • constructor는 메서드가 아닌 클래스가 평가되어 생성한 함수 객체 코드의 일부로 해석된다.
  • constructor는 클래스 내 최대 한 개만 존재할 수 있다.
  • constructor 생략시 빈 constructor가 암묵적으로 생성된다.
  • constructor 내부에서 this에 추가한 프로퍼티는 인스턴스 프로퍼티가 된다.
  • constructor 내부에서 this가 아닌 다른 값을 반환하는 것은 클래스 기본 동작을 훼손하기에 return문은 반드시 생략해야 한다.

예시) 

// 클래스
class Member {
    // 생성자
    constructor(name) {
      // 인스턴스 생성 및 초기화
      this.name = name;
    }
}

 

2. 프로토타입 메서드

  • 프로토타입 메서드는 클래스로 생성된 인스턴스들이 공유하는 메서드이다.
  • 클래스의 정의 내부에서 메서드를 선언하면, 이 메서드는 클래스의 프로토타입에 저장되며, 클래스로 생성된 모든 인스턴스가 이 프로토타입 메서드를 공유하여 사용할 수 있다.
  • 메모리를 효율적으로 사용할 수 있다.
  • 생성자 함수때와는 다르게 prototype 프로퍼티에 메서드를 추가하지 않아도 정의할 수 있다.
  • 클래스에 생성한 인스턴스는 프로토타입 체인의 일원이 된다.

예시) 

// 클래스
class Member {
    // 생성자
    constructor(name, age) 
      // 인스턴스 생성 및 초기화
      this.name = name;
      this.age = age;
    }
  
    // 프로토 타입 메서드
    sayHello() {
        console.log(`안녕하세요, 저는 ${this.name}이고, ${this.age}살입니다.`);
    }
}

const member = new Member('sjmoon','28');
member.sayHello(); // 안녕하세요, 저는 sjmoon이고, 28살입니다.

 

3. 정적 메서드

  • 정적 메서드는 클래스 자체에 속한 메서드(클래스에 바인딩된 메서드)이다.
  • 인스턴스를 생성하지 않고도 클래스 이름을 통해 직접 호출할 수 있다.
  • 클래스와 관련된 유틸리티 함수를 정의하는 데 사용된다.
  • 정적 메서드를 생성하기 위헤서는 명시적으로 static 키워드를 선언해야 한다.
  • 정적 메서드는 프로토타입 체인 상에 존재하지 않기에 인스턴스로 호출할 수 없다.

예시)

class Member {
    // 생성자
    constructor(name, age) {
      this.name = name;
      this.age = age;
    }

    // 정적 메서드
    static sayHello() {
      console.log(`안녕하세요!`);
    }
}

Member.sayHello(); // 안녕하세요!

 

 

클래스의 인스턴스 생성과정

1. 인스턴스 생성과 this 바인딩

클래스의 생성자 함수가 호출되기에 앞서 암묵적으로 빈객체가 생성되는데 이때 생성된 객체가 클래스의 인스턴스다. 생성자 함수 내부에서 사용되는 this에 인스턴스가 바인딩되므로 내부의 this는 인스턴스를 가리킨다.

 

2. 인스턴스 초기화

생성자 함수 내부에서 this에 바인딩되어 있는 인스턴스의 초기화 작업을 수행한다. 이 단계에서 인스턴스의 속성(데이터 멤버)을 설정하거나 초기값을 할당할 수 있다.

 

3. 인스턴스 반환

생성자 함수 내의 초기화 작업이 완료되면 명시적인 반환문이 없어도 this가 자동으로 반환된다. 

 

예시)

class MyClass {
    constructor(param1, param2) {
      // 1. 인스턴스 생성과 this 바인딩
      // 새로운 객체가 생성되고, this는 이 객체를 가리킴
      this.param1 = param1;
      
      // 2. 인스턴스 초기화
      // 생성자 내에서 인스턴스의 속성을 설정
      this.initialValue = 0;
    }
    
    // 클래스 메서드 정의
    someMethod() {
      console.log("This is a method of the instance.");
    }
  }
  
  // 클래스의 생성자 함수를 호출하여 인스턴스를 생성
  const instance = new MyClass('value1', 'value2');
  
  // 3. 인스턴스 반환
  // 생성자 함수 내부에서 this를 반환하지 않아도
  // instance는 생성된 인스턴스를 참조함
  console.log(instance.param1); // 출력: value1
  console.log(instance.initialValue); // 출력: 0
  instance.someMethod(); // 출력: This is a method of the instance.

 

 

클래스의 프로퍼티

1. 인스턴스 프로퍼티

  • 인스턴스 프로퍼티는 클래스 내에서 생성자 메서드(constructor) 내부에서 this 키워드를 사용하여 선언된 변수들을 말한다.
  • 클래스 내에서 생성자를 통해 선언된 변수들은 인스턴스가 생성될 때마다 각각의 인스턴스에 해당 프로퍼티가 새로 생성되어 할당된다.
  • 인스턴스 프로퍼티는 클래스의 모든 인스턴스에서 공유되지 않으며, 각 인스턴스마다 다른 값을 가질 수 있다.

예시)

class Member {
  constructor(name, age) {
    this.name = name; // 인스턴스 프로퍼티
    this.age = age; // 인스턴스 프로퍼티
  }
}

 

2. 접근자 프로퍼티(Getter / Setter)

  • 접근자 프로퍼티는 값을 갖지 않고 다른 데이터의 프로퍼티 값을 읽거나 저장할 때 사용하는 함수로 구성된 프로퍼티이다. 이를 이용해 프로퍼티 값을 읽거나 쓸 때 추가적인 동작을 수행할 수 있다.
  • 클래스 내에서 get과 set 키워드를 사용하여 선언된 프로퍼티로 get은 해당 프로퍼티 값을 가져오는 메서드를 정의하고, set은 해당 프로퍼티 값을 설정하는 메서드를 정의한다.
  • set은 단 하나의 매개변수만 선언할 수 있다.

예시)

class Member {
  constructor(name, age) {
    this._name = name; // 인스턴스 프로퍼티 (실제 값은 _name에 저장)
  }

  // Getter 접근자 프로퍼티
  get name() {
    return this._name;
  }

  // Setter 접근자 프로퍼티
  set name(value) {
    this._name = value;
  }
}

 

3. 클래스 필드

  • 클래스 필드는 클래스 내에서 선언된 멤버 변수로, 클래스의 인스턴스가 가지는 속성을 정의하는 데 사용된다.
  • 프로퍼티를 표현하는 역할을 하며, 클래스의 메서드에서 사용되는 데이터를 저장할 수 있다.
  • 클래스 필드를 참조하는 경우 this를 반드시 사용해야 한다.
  • 초기값을 할당하지 않으면 undefined가 할당된다.
  • 클래스 필드 정의 제안은 아직 ECMAScript의 정식 표준 사양이 아니다. 하지만 최신 브라우저(Chrome 72 이상)와 최신 Node.js(12 이상)에서 실행하면 정상적으로 실행된다.
  • 함수를 클래스 필드에 할당할 수 있으나 이 함수는 프로토타입 메서드가 아닌 인스턴스 메서드가 되므로 메모리 손해가 일어날 수 있다. 그러므로 클래스 필드에 함수를 할당하는 것은 권장하지 않는다.

예시)

class Member {
  // 클래스 필드 (멤버 변수) 선언
  name = '';
  age = 0;

  // 생성자 메서드
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  // 인스턴스 메서드
  introduce() {
    console.log(`안녕하세요 제 이름은 ${this.name}이고, ${this.age}살 입니다.`);
  }
}

const member1 = new Member('sjmoon', 28);
const member2 = new Member('jjmoon', 23);

member1.introduce(); // 출력: 안녕하세요 제 이름은 sjmoon이고, 28살 입니다.
member2.introduce(); // 출력: 안녕하세요 제 이름은 jjmoon이고, 23살 입니다.

 

 

4. private 필드 정의

  • 자바스크립트에서느  다른 클래스 기반 객체지향 언어에서 지원하는 private, public, protected 키워드와 같은 접근제한자를 지원하지 않았으나 TC39 프로세스의 stage 3에는 private 필드를 정의할 수 있는 방법이 도입되었다. 
  • # 기호를 사용하여 private 필드를 선언할 수 있다.
  • private 필드는 클래스 내부에서만 접근할 수 있으며, 클래스 외부에서는 직접 접근할 수 없다.
  • private 필드는 반드시 클래스 몸체에 적용해야 한다. construcrot에 적용하면 에러가 발생한다.

예시)

class Member {
  #name; // private 필드

  constructor(name) {
    this.#name = name; // private 필드 초기화
  }

  getName() {
    return this.#name; // private 필드에 접근하는 메서드
  }
}

const member = new Member('sjmoon');
console.log(member.getName()); // "sjmoon"
//console.log(person.#name); // SyntaxError: Private field '#name' must be declared in an enclosing class

 

4-1. private와 public

접근 private public
클래스 내부 O O
자식 클래스 내부 X O
인스턴스를 통한 접근 X O

 

5. static 필드 정의

  • 클래스에 속하지만 인스턴스마다 독립적이지 않고 클래스 레벨에서 공유되는 필드를 말한다.
  • 클래스의 모든 인스턴스가 해당 static 필드의 값을 공유한다.
  • static 필드는 인스턴스가 아닌 클래스 자체에 속하며, 클래스의 생성자와 메서드에서 직접 접근할 수 있다.
  • 클래스 내부에 static 키워드를 사용하여 선언한다.

예시)

class MathUtil {
  static PI = 3.14159265359; // static 필드

  static add(a, b) {
    return a + b;
  }

  static multiply(a, b) {
    return a * b;
  }
}

console.log(MathUtil.PI); // 3.14159265359

console.log(MathUtil.add(5, 3)); // 8
console.log(MathUtil.multiply(5, 3)); // 15
반응형

+ Recent posts