클래스가 등장하기 전

자바스크립트는 프로토타입 기반으로 객체지향 언어이고 강력한 객체 지향 프로그래밍 능력을 지니고 있다.
프로토타입 기반 객체지향 언어는 클래스가 필요없는 객체 지향 프로그래밍 언어다. ES5에서는 클래스 없이도 생성자 함수와 프로토타입을 통해 객체지향 언어의 상속을 구현 할 수있다.

클래스의 등장

ES6에서 도입된 클래스는 클래스 기반 객체지향 프로그래밍언어와 매우 흡사한 새로운 객체 생성 메커니즘이다. 사실 클래스는 함수이며 기존 프로토타입 기반 패턴을 클래스 기반 패턴처럼 사용 할 수 있도록 하는 문법적 설탕이다.
단, 클래스와 생성자 함수 모두 프로토타입 기반의 인스턴스를 생성하지만 정확히 동일하게 동작하지는 않고 클래스는 생성자 함수보다 엄격하며 생성자 함수에서는 제공하지 않는 기능도 제공한다.

클래스는 생성자 함수와 매우 유사하게 동작하지만 몇 가지 차이가 있다.

  • 클래스를 new 연산자 없이 호출하면 에러가 발생한다. 하지만 생성자 함수를 new 연산자 없이 호출하면 일반 함수로서 호출된다.
  • 클래스는 상속을 지원하는 extendssuper 키워드를 제공한다.
  • 클래스 내의 모든 코드에는 암묵적으로 strict mode가 지정되어 실행되며 strict mode를 해제할 수 없다. 생성자 함수는 암묵적으로 지정되지 않는다.
  • 클래스의 contructor, 프로토타입 메서드, 정적 메서드는 모두 프로퍼티 어트리뷰트 [[Enumerable]]의 값이 false다. 다시 말해, 열거 되지 않는다.

    위 동작들의 차이를 보면 클래스는 프로토타입 기반 객체 생성 패턴의 단순함 문법적 설탕이라기 보다는 새로운 객체 생성 메커니즘이라 생각할 수 있다.

클래스 정의

  • 클래스는 class 키워드를 사용하여 정의한다.
  • 클래스는 함수이며 일급 객체다.
  • 클래스 몸체에 0개 이상의 메서드를 정의 할 수 있다. 몸체 정의할 수 있는 메서드의 종류는 contructor, 프로토타입 메서드, 정적 메서드 이렇게 3가지 메서드가 있다.

클래스 호이스팅

class Person {}
 
console.log(typeof Person); // function
  • 클래스는 함수로 평가 되기에 클래스 선언 문으로 정의한 클래스는 함수 선언문과 같이 런타임 이전에 소스코드 평가 과정을 거쳐 함수 객체를 생성한다.
  • 이때 생성된 생성자는 constructor( 생성자와 프로토타입은 쌍으로 존재하기 때문에 이때 프로토 타입도 생성됨)
  • 클래스는 정의 이전에 참조 할 수 없다. (호이스팅이 되지 않은 것 처럼 보이나 이는 let, const 처럼 TDZ개념이 존재하기 때문이다)
  • 즉, 클래스도 호이스팅이 발생한다.

인스턴스 생성

  • 클래스는 생성자 함수이며, new연산자와 함께 호출되어 인스턴스 생성
class Person {}

// 인스턴스 생성
const me = new Person ();
console.log(me); // Person {}
  • new 연산자 없이 호출하면 타입 에러 발생한다.
  • 기명 함수 표현식과 마찬가지로 클래스 표현식에 사용한 클래스 이름은 외부 코드에서 접근 불가능하다.
const Person = class MyClass {};
 
// 함수 표현식과 마찬가지로 클래스를 가리키는 식별자로 인스턴스를 생성해야 한다.
const me = new Person();
 
// 클래스 이름 MyClass는 함수와 동일하게 클래스 몸체 내부에서만 유효한 식별자다.
console.log(MyClass); // ReferenceError: MyClass is not defined
 
const you = new MyClass(); // ReferenceError: MyClass is not defined

메서드

클래스 몸체에는 0개 이상의 메서드만 선언할 수 있다. 몸체 정의할 수 있는 메서드의 종류는 contructor, 프로토타입 메서드, 정적 메서드 의 세 가지가 있다.

  1. contructor
  • contructor는 인스턴스를 생성하고 초기화하기 위한 특수한 메서드다. contructor는 이름을 변경할 수 없다.
  • contructor는 메서드로 해석되는 것이 아니라 클래스가 평가되어 생성한 함수 객체 코드의 일부가 된다. 즉, 클래스 정의가 평가되면 constructor의 기술된 동작을 하는 함수 객체가 생성된다.
  • 클래스의 constructor 메서드와 프로토타입의 constructor 프로퍼티는 직접 적인 관련 없다. 프로토타입의 constructor 프로퍼티는 모든 프로토타입이 가지고 있는 프러퍼티 이며, 생성자 함수를 가리킨다.
class Person {
	// 생성자
  constructor(name) {
  	// 인스턴스 생성 및 초기화
    this.name = name;
  }
}
  • contructor는 생성자 함수와 유사하지만 몇가지 차이가 있다.
    • constructor는 클래스 내에 최대 한 개만 존재 할 수 있다.
    • constructor를 생략하면 클래스에 빈 constructor가 암묵적으로 정의된다.
    • 프로퍼티가 추가되어 초기화 된 인스턴스를 생성하려면 constructor 내부에서 this에 인스턴스 프로퍼티를 추가한다.
    • constructor 내부에서 반환return 을 반드시 생략한다.
  1. 프로토타입 메서드
    클래스 몸체에서 정의한 메서드는 생성자 함수에 의한 객체 생성 방식과는 다르게 클래스의 prototype 프로퍼티에 메서드를 추가하지 않아도 기본적으로 프로토타입 메서드가 된다.
class Perosn {
	// 생성자
  constructor(name) {
  	// 인스턴스 생성 및 초기화
    this.name = name;
  }
  
  // 프로토타입 메서드
  sayHi() {
    console.log(`Hi ! My name is ${this.name}`);
  }
}

const me = new Perosn('Lee')
me.sayHi(); // Hi ! My name is Lee

생성자 함수와 마찬가지로 클래스가 생성한 인스턴스는 프로토타입 체인의 일원이 된다.

// me 객체의 프로토타입은 Person.prototype이다.
Object.getPrototypeOf(me) === Person.prototype; // true
me instanceof Person; // true

// Person.prototype의 프로토타입은 Object.prototype이다.
Object.getProtototypeOf(Person.prototype) === Object.prototype; // true
me instanceof Object; // true

// me  객체의 contructor는 Person 클래스다.
me.constructor === Person; // true

클래스는 생성자 함수와 같이 인스턴스를 생성하는 생성자 함수라고 볼 수 있다. 즉, 클래스는 프로토타입 기반의 객체 생성 메커니즘이다.

  1. 정적 메서드
  • 정적메서드는 인스턴스를 생성하지 않아도 호출할 수 있는 메서드를 말한다.
  • 생성자 함수 경우 정적 메서드를 생성하기 위해서는 명시적으로 생성자 함수에 메서드를 추가해야 한다.
  • 클래스에서는 메서드에 static 키워드를 붙이면 정적 메서드(클래스 메서드)가 된다.
class Person {
	// 생성자
  constructor(name) {
  	// 인스턴스 생성 및 초기화
    this.name = name;
  }
  
  // 정적 메서드
  static sayHi() {
  	console.log("Hi");
  }
}
// 정적 메서드는 클래스로 호출된다.
// 정적 메서드는 인스턴스 없이 호출 가능 !
Person.sayHi() // Hi !

정적 메서드와 프로토타입 메서드의 차이

  • 정적 메서드와 프로토타입 메서드는 자신이 속해 있는 프로토타입 체인이 다르다.
  • 정적 메서드는 클래스로 호출하고 프로토타입 메서드는 인스턴스로 호출한다.
  • 정적 메서드는 인스턴스 프로퍼티를 참조할 수 없지만 프로토타입 메서드는 인스턴스 프로퍼티를 참조할 수 있다.
  • this 바인딩 원리에 따라 정적 메서드는 클래스로 호출 하기 때문에 내부의 인스턴스 프로퍼티를 참조 할 수 없습니다.

클래스에서 정의한 메서드의 특징
1. function 키워드를 생략한 메서드 축약 표현을 사용한다.
2. 객체 리러털과는 다르게 클래스에 메서드를 정의할 때는 콤마가 필요없다.
3. 암묵적으로 strict mode로 실행된다.
4. for...in문이나 Object.key 메서드 등으로 열거할 수 없다. 즉, 프로퍼티의 열거 기능 여부를 나타내며, 불리언 값을 갖는 프로퍼티 어트리뷰트 [[Enumberable]]의 값이 false다.
5. 내부 메서드 [[Construct]]를 갖지 않는 non-constructor다. 따라서 new연산자와 함께 호출할 수 없다.

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

  1. 인스턴스 생성과 this 바인딩
  • new 연산자와 함께 클래스를 호출하면 constructor의 내부 코드가 실행되기에 앞서 암묵적으로 빈 객체 생성된다. 이 빈 객체가 클래스가 생성한 인스턴스다.
  • 이때, 클래스가 생성한 인스턴스의 프로토타입으로 클래스의 prototype 프로퍼티가 가리키는 객체가 설정된다.
  • 암묵적으로 생성된 빈 객체. 즉, 인스턴스는 this에 바인딩된다. 따라서 constructor 내부의 this는 클래스가 생성한 인스턴스를 가리킨다.
  1. 인스턴스 초기화
  • constructor의 내부 코드가 실행 this에 바인딩되어 있는 인스턴스를 초기화한다.
    즉, this에 바인딩되어 있는 인스턴스에 프로퍼티를 추가하고 constructor가 인수로 전달받은 초기값으로 인스턴스의 프로퍼티 값을 초기화한다.
  1. 인스턴스 반환
    클래스의 모든 처리가 끝나면 완성된 인스턴스가 바인디된 this가 암묵적으로 반환된다.
class Person {
	// 생성자
  	constructor(name) {
    	// 1. 암묵적으로 인스턴스가 생성되고 this에 바인딩된다.
      console.log(this); // Perosn {}
      console.log(Object.getPrototypeOf(this) === Person.prototype); // true
      
      // 2. this에 바인딩되어 있는 인스턴스를 초기화한다.
      this.name = name;
      
      // 3. 완성된 인스턴스가 바인딩된 this가 암묵적으로 반환된다.
    }
}

프로퍼티

constructor 가 생성자 함수의 역할을 수행한다. 그러므로 인스턴스 프로퍼티는 constructor 내부에 정의 해야한다.

ES6의 클래스의 인스턴스의 프로퍼티는 항상 public 이다.
private 프로퍼티를 정의할 수 있는 사양이 제안 중에 있다.

접근자 프로퍼티

자체적으로 값을 갖지 않고, 다른 데이터의 값을 읽거나 저장할 때 자동으로 쓰여지는 접근자 함수이다. 접근자 프로퍼티 역시 프로토타입의 프로퍼티가 된다.

const person = {
	firstName = "kyungBin",
  	lastName = "Park",
  	get fullName() {
    	return `${this.lastName} ${this.firstName}`
    }
	set fullName(name){
    	[this.lastName, this.firstName] = name.split(" ");
    }
}
person.fullName //  Park kyungBin
person.fullName = "Lee woongMo" // person.fullName에 들어감
// person.fullName() //x
// person.fullName // o

클래스 필드 정의

클래스 필드는 클래스 기반 객체지향 언어에서 클래스가 생성할 인스턴스의 프로퍼티를 가리키는 용어이다. Java 에서 클래스 필드는 마치 클래스 내부에서 변수처럼 사용한다.

class Person {
  // 값을 할당하지 않으면 undefined가 할당된다.
	name = 'lee';
	// this.name = ""; 이렇게 선언은 불가능하다. contructor와 메서드 내에서만 this 사용이 가능하다.
	constructor(){
    	console.log(this.name) // 자바에서는 this를 떼도 호출되지만, js는 무조건 this를 붙여야한다.
    }
}
const me = new Person();
me.name // lee

함수도 할당이 가능하다. JS 함수는 일급 객체이기 때문이다.

class Person{
	name = "lee";
	//클래스 필드에 함수를 할당했다. 메소드가 아니다.
	getName = function() {
    	return this.name;
    }
}

클래스 필드는 인스턴스의 프로퍼티이기 때문에 프로토타입 메서드가 되지 않기에 권장하지 않는다.

private 필드

private 필드 또한 ES2019 을 통해 등장했다. private 필드는 접두사로 # 가 붙는다.

class Person {
	#name = "";
	constructor(name) {
		this.#name = name // private 필드에 접근할 때도 # 를 붙인다.
	}
}
profile
꺾여도 하는 마음

0개의 댓글