[JS-책 편김에 끝까지]클래스

짱쫑·2023년 2월 21일
0

javascript

목록 보기
8/10
post-thumbnail

자바스크립트는 프로토타입 기반 객체지향 언어다. 프로토타입 기반 객체지향 언어는 클래스가 필요없는 객체지향 언어다.

let person = (function() {
	function Person(name) {
  	this.name = name;
  }
  
  // 프로토타입 메서드
  Person.prototype.sayHi = function() {
  	console.log(`Hi~ I'm ${this.name}`);
  }
  return Person;
}());

let me = new Person('zzangzzong')';
me.sayHi(); // Hi~ I'm zzangzzong

기존의 클래스 기반 언어와의 차이점이다.
프로토타입 기반인 자바스크립트에서 클래스는 처음부터 존재했던것이 아니다. ES6에서 도입되었고 클래스 기반 객체지향 언어와 매우 흡사한 새로운 객체 생성 메커니즘을 제시하게 되었다.
-javascript

클래스 class ⭐️ ⭐️ ⭐️

클래스는 함수이며 기존 프로토타입 기반 패턴을 클래스 기반 패턴처럼 사용할 수 있도록 하는 문법적 설탕(Syntatic sugar)이라고 볼 수 있다.

❗️ 클래스와 생성자 함수는 모두 프로토타입 기반의 인스턴스를 생성하지만 정확히 동일하게 동작하지는 않는다. 클래스느 생성자 함수보다 엄격하며 생성자 함수에서는 제공하지 않는 기능도 제공한다.

  • 차이점
    • 클래스를 new 연산자 없이 호출하면 에러가 발생한다. 하지만 생성자 함수를 new 연산자 없이 호출하면 일반 함수로서 호출된다.
    • 클래스는 상속을 지원하는 extends와 super키워드를 제공한다. 하지만 생성자 함수는 extends와 super를 제공하지 않는다.
    • 클래스는 호이스팅이 발생하지 않는 것처럼 동작한다. 하지만 함수 선언문으로 정의된 생성자 함수는 함수 호이스팅이, 함수 표현식으로 정의한 생성자 함수는 변수 호이스팅이 발생한다.
    • 클래스 내의 모든 코드에는 암묵적으로 strict mode가 지정되어 실행되며 strict mode를 해제할 수 없다. 하지만 생성자 함수는 암묵적으로 strict mode가 지정되지 않는다.
    • 클래스의 constructor, 프로토타입 메서드, 정적 메서드는 모두 프로퍼티 어트리뷰트 [[Enumerable]]의 값이 false다. 즉, 열거되지 않는다는 말이다.

클래스는 쉽게말해 객체를 생성할 수 있는 템플릿(청사진, 틀)이라고 생각하면 쉽고 객체지향 프로그래밍이 가능하다.

// 클래스 선언문
class Person { }

// 익명 클래스 표현식
const Person = class { };

// 기명 클래스 표현식
const Person = class MyClass { };

클래스를 표현식으로 정의할 수 있다는 것은 클래스가 값으로 사용할 수 있는 일급 객체이며 클래스는 일급 객체로서 다음과 같은 특징을 갖는다.

  • 무명의 리터럴로 생성할 수 있다. 즉, 런타임에 생성이 가능하다
  • 변수나 자료구조(객체, 배열 등)에 저장할 수 있다.
  • 함수의 매개변수에게 전달할 수 있다.
  • 함수의 반환값으로 사용할 수 있다.

클래스 기본

// 클래스 선언문
class Fruit {
  // 생성자: new 키워드로 객체를 생성할 때 호출되는 함수
  // 생성자는 클래스 내 한 개만 존재할 수 있음. 2개 이상이면 문법에러 발생
	constructor(name, emoji) {
  	// 인스턴스 생성 및 초기화
  	this.name = name; // name 프로퍼티는 public하다
      this.emoji = emoji;
	}      
  // 함수 선언
  display = () => {
	  console.log(`${this.name}: ${this.emoji}`);
  }
}
// 인스턴스 (클래스를 통해서 만들어진 객체)
const apple = new Fruit('apple', '🍎');
const orange = new Fruit('orange', '🍊');
console.log(apple); // Fruit { display: [Function: display], name: 'apple','🍎' }
console.log(orange); // Fruit { display: [Function: display], 'orange', '🍊'}
apple.display(); // apple: 🍎
  • 인스턴스 생성과 this바인딩
    new 연산자와 함께 클래스를 호출하면 함수와 마찬가지로 클래스 내부 메서드 [[Contruct]]가 호출되는데 클래스는 new 연산자 없이 호출할 수 없다. new 연산자와 함께 함수를 호출하면 constructor 내부 코드가 실행되기 전 암묵적으로 빈 객체가 생성된다. 이 빈 객체가 바로 클래스가 생성한 인스턴스이다. 이때 클래스가 생성한 인스턴스의 프로토타입으로 클래스의 prototype 프로퍼티가 가리키는 객체가 설정된다. 그리고 암묵적으로 생성된 빈 객체==인스턴스는 this에 바인딩 된다. 따라서 constructor 내부의 this는 클래스가 생성한 인스턴스를 가리키게 된다.

  • 인스턴스 초기화
    constructor 내부 코드가 실행되어 this에 바인딩되어 있는 인스턴스를 초기화하는 것이다. this에 바인딩되어 있는 인스턴스에 프로퍼티를 추가하고 constructor가 인수로 전달받은 초기값으로 인스턴스의 프로퍼티 값을 초기화한다.

정적 프로퍼티 static property

static makeRandomFruit() {
	// 클래스 레벨의 메서드에서는 this를 참조할 수 없음 (템플릿 상태이기 때문)
  return new Fruit('banana', '🍌');
}
const banana = Fruit.makeRandomFruit();
console.log(banana); // Fruit { display: [Function: display], 'banana', '🍌'}

클래스 레벨의 함수는 클래스 이름으로 접근이 가능하고, 인스턴스 레벨의 프로퍼티와 함수는 만들어진 인스턴스를 통해서 접근이 가능하다. static을 붙이면 클래스 레벨로 만들 수 있다(함수 앞에 붙이면 클래스 레벨의 메서드임).

class Fruit {
	static MAX_FRUITS = 4;
	constructor(name, emoji) {
  	this.name = name; // name 프로퍼티는 public하다
      this.emoji = emoji;
	}      
  display = () => {
	  console.log(`${this.name}: ${this.emoji}`);
  }
}
console.log(Fruit.MAX_FRUITS); // 4

클래스 필드 class field

클래스 필드(필드 또는 멤버)는 클래스 기반 객체지향 언어에서 클래스가 생성할 인스턴스의 프로퍼티를 기리키는 용어이다. ㅠ

class Person {
  // 클래스 필드 정의
  name = 'zzangzzong';
}

const me = new Person();
console.log(me); // Person { name: 'zzangzzong' }

접근제한자 - 캡슐화

캡슐화 encapsulation 는 객체의 상태를 나타내는 프로퍼티와 프로퍼티를 참조하고 조작할 수 있는 동작 behavior 인 메서드를 하나로 묶는 것을 말하는데 캡슐화는 객체의 특정 프로퍼티나 메서드를 감출 목적으로 사용하기도 한다. 이를 정보 은닉 information hiding 이라 한다.

정보 은닉은 외부에 공개할 필요가 없는 구현의 일부를 외부에 공개되지 않도록 감추어 적절치 못한 접근으로부터 객체의 상태가 변경되는 것을 방지하고 정보 보호, 객체간의 상호 의존성 즉 결합도 coupling 를 낮추는 효과가 있다.

java와 같은 객체지향 언어는 클래스를 정의하고 그 클래스를 구성하는 멤버에 대하여 public, private, protected와 같은 접근 제한자 키워드를 사용하여 공개 범위를 한정할 수 있다. public으로 선언된 프로퍼티와 메서드는 클래스 외부에서 참조할 수 있지만 private은 클래스 내부에서만 참조할 수 있다(외부에서 참조 x).

하지만 자바스크립트는 public, private, protected와 같은 접근 제한자를 제공하지 않는다. 그러므로 자바스크립트 객체의 모든 프로퍼티와 메서드는 기본적으로 외부에 공개되어 있다. 즉 객체의 모든 프로퍼티와 메서드는 기본적으로 public 이라는 말이다.

그러나 ES2019에 접근제한자 관련 문법이 추가 되었다.
바로 '#' 해시를 이용하는 것인데, #은 키워드가 아닌 '프리픽스'라고 한다.

class Fruit {
  constructor(name, emoji) {
    this.#name = name;
    this.#emoji = emoji;
  }      
  display = () => {
	  console.log(`${this.#name}: ${this.#emoji}`);
  }
}
const apple = new Fruit('apple', '🍎');
apple.#name = 'orange'; 
// SyntaxError: Private field '#name' must be declared in an enclosing class
// 비공개 필드 '#name'은 클래스 안에서만 선언되어야 한다
console.log(apple); // Fruit { display: [Funcion: display] }
// 함수에서도 #을 붙이면 감출 수 있다.
ex) #display = () => { }
console.log(apple); // Fruit { }

접근자 프로퍼티 accessor property

class Student {
	constructor(firstName, lastName) {
  	  this.firstName = firstName;
      this.lastName = lastName;
  }
  fullName() {
  	return `${this.lastName} ${this.firstName}`
  }
}
const student = new Student('zzang', 'zzong');
console.log(student.fullName()); // zzong zzang

fullName을 함수로 호출하고 있다. 프로퍼티로 만들어서 사용하면 안되는 것일까? 일단 가능은 하다. 함수 대신 this.fullName = `${this.lastName} ${this.firstName}` 과 같이 만들어서 console.log(student.fullName);을 해보면 콘솔창에 잘 나오는 것을 확인할 수 있다. 그러나 만약 student.lastName에 직접 접근해서 값을 바꿔 버린다면 어떨까? lastName이 업데이트가 되어도 fullName은 이를 알아차리지 못한다. 그래서 함수로 만들어서 쓰는 것이다.

class Student {
	constructor(firstName, lastName) {
  	  this.firstName = firstName;
      this.lastName = lastName;
      this.fullName = `${this.lastName} ${this.firstName}`;
  }
}
const student = new Student('zzang', 'zzong');
student.lastName = 'noah';
console.log(student.lastName); // noah
console.log(student.fullName()); // zzong zzang
// 변경사항이 업데이트되지 않음

코드로 봐도 생성자로 한번 선언되고 나서는 업데이트가 되지 않는걸 알 수 있다.

그래도 함수호출이 아닌 일반적으로 접근하는 것처럼 하고 싶다고 한다면 접근자 프로퍼티를 사용하면 된다.

  • getter

    class Student {
    	constructor(firstName, lastName) {
      	  this.firstName = firstName;
          this.lastName = lastName;
      }
      get fullName() {
      	return `${this.lastName} ${this.firstName}`
      }
    }
    const student = new Student('zzang', 'zzong');
    console.log(student.fullName); // zzong zzang
    // 함수를 호출하는 것처럼 보이지 않지만 정상적으로 값이 나오는걸 확인할 수 있다

    접근자 프로퍼티를 쓰면 함수지만 고정된 값이 아니라 호출하는 시점에 데이터를 만들어서 리턴을 하는데 이것은 특정한 속성을 가져와서 보여주는 것이기 때문에 속성의 한 부분이라고 간주할 수 있다.

  • setter

    set은 할당할 때 사용할 수 있다.

    class Student {
    	constructor(firstName, lastName) {
      	  this.firstName = firstName;
          this.lastName = lastName;
      }
      get fullName() {
      	return `${this.lastName} ${this.firstName}`
      }
      set fullName(value) {
      	return `${this.lastName} ${this.firstName}`
      }
    }
    const student = new Student('zzang', 'zzong');
    console.log(student.fullName); // zzong zzang
    student.fullName = 'francois';
    console.log(student.fullName); // francois

함수가 아닌 접근자 프로퍼티를 사용하는 경우는 어떤 로직을 실행하는 것이 아닌 무언가를 조합해서 만들거나 특정한 숫자를 set할 때 0이하의 값이 value로 들어온다면 0으로 return한다거나 가볍게 쓸 수 있다.

상속 extends

클래스는 상속을 통해 다른 클래스를 확장할 수 있는 문법인 extends 키워드가 기본적으로 제공된다.

class Animal {
	constructor(color) {
  	this.color = color;
  }
  eat() {
	  console.log('먹는다');
  }
  sleep() {
	  console.log('잔다');
  }
}

class Tiger extends Animal { }
const tiger = new Tiger('얼룩이');
console.log(tiger); // Tiger { color: '얼룩이' }
tiger.sleep(); // 잔다
tiger.eat(); // 먹는다

class Dog extends Animal {
	constructor(color, ownerName) {
  	super(color); // 부모생성자를 가리킴
      this.ownerName = ownerName;
  }
  play() {
  	console.log('논다');
  }
  
  // ❗️ 오버라이딩 overriding - 자식클래스에서 부모클래스의 인스턴스를 덮어씌운다
  eat() {
  	console.log('개가 먹는다');
  }
  
  // 부모의 인스턴스를 불러오고 난 이후에 내가 원하는 것을 할 수도 있다.
  // eat() {
  //   super.eat();
  //   console.log('개가 먹는다')'
  // }
}
const dog = new Dog('꺼멍이', '짱쫑');
console.log(dog); // { color: '꺼멍이', ownerName: '짱쫑' }

하지만 생성자 함수에서는 클래스와 같이 상속을 통해 다른 생성자 함수를 확장할 수 있는 문법이 제공되지 않는다. 자바스크립트는 클래스 기반 언어가 아니므로 생성자 함수를 사용해서 클래스를 흉내 내려는 시도를 권장하지 않지만 의사 클래스 상속 pseudo classical inheritance 패턴을 사용해서 흉내정도는 낼 수 있다.

profile
不怕慢, 只怕站

0개의 댓글