클래스와 생성자 함수의 차이점
1. new 연산자 없이 호출시, 클래스는 에러 발생. 생성자 함수는 일반 함수로 호출
2. 상속을 지원하는 extends와 super 키워드 제공의 유무, 클래스는 제공O. 생성자 함수는 제공X
3. 호이스팅, 클래스는 호이스팅이 발생하지 않는 것처럼 동작. 생성자 함수는 함수 선언문의 경우 함수 호이스팅으로, 함수 표현식의 경우 변수 호이스팅이 발생함.
4. strict mode, 클래스는 모든 코드에 암묵적으로 strict mode가 지정됨. 생성자 함수는 암묵적으로 strict mode가 지정되지 않음
5. 클래스의 constructor, 프로토타입 메서드, 정적 메서드는 모두 프로퍼티 어트리뷰트 [[Enumerable]]의 값이 false임. 열거 되지 않음.
// 클래스 선언문
class Person {}
// 익명 클래스 표현식
const Person = class {};
// 기명 클래스 표현식
const Person = class MyClass {};
// 클래스 선언문
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!
const Person = class MyClass {};
const me = new Person();
// ReferenceError
const you = new MyClass();
class Person {
// 생성자
constructor(name) {
// 인스턴스 생성 및 초기화
this.name = name;
}
}
생성자 함수와의 차이
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.
// me 객체의 프로토타입은 Person.prototype이다
Object.getPrototypeOf(me) === Person.prototype // true
me instanceof Person; // true
// Person.prototype 객체의 프로토타입은 Object.prototype이다
Object.getPrototypeOf(Person.prototype) === Object.prototype // true
me instanceof Object; // true
// me 객체의 constructor는 Person 클래스다
me.constructor === Person; // true
class Person {
// 생성자
constructor(name) {
// 인스턴스 생성 및 초기화
this.name = name;
}
// 정적 메서드
static sayHello() {
console.log('Hello!');
}
}
Person.sayHello(); // Hello!
const me = new Person('Lee');
me.sayHello(); // TypeError
//정적 메서드는 인스턴스 프로퍼티 참조x
class Square {
// 정적 메서드
static area(width, height) {
return width * height;
}
}
console.log(Square.area(10, 10)); // 100
// 프로토타입 메서드는 인스턴스 프로퍼티 참조o
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
Math.max(1, 2, 3); // 3
Number.isNaN(NaN); // true
JSON.stringify({a: 1}); // "{"a":1}"
1. 인스턴스 생성과 this 바인딩
2. 인스턴스 초기화
3. 인스턴스 반환
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
// getter 함수
get fullName() {
return `${this.firstNmae} ${this.lastName}`;
}
// setter 함수
set fullName(name) {
[this.firstName, this.lastName] = name.split(' ');
}
}
const me = new Person('Ungmo', 'Lee');
// 데이터 프로퍼티를 통한 프로퍼티 값의 참조
console.log(`${me.firstName} ${me.lastName}`); // Ungmo Lee
// 접근자 프로퍼티를 통한 프로퍼티 값의 저장
me.fullName = 'Heegun Lee';
console.log(me); // {firstName: "Heegun", lastName: "Lee"}
// 접근자 프로퍼티를 통한 프로퍼티 값의 참조
console.log(me.fullName); // Heegun Lee
class Person {
// 클래스 필드 정의
name = 'Lee';
}
const me = new Person();
console.log(me); // Person {name: 'Lee'}
class Person {
// SyntaxError
this.name = 'Lee';
}
class Person {
name = 'Lee';
constructor() {
console.log(name); // ReferenceError
}
}
class Person {
name;
constructor(name) {
this.name = name;
}
}
const me = new Person('Lee');
console.log(me); // Person {name: 'Lee'}
class Person {
// 클래스 필드에 문자열을 할당
naem = 'Lee';
// 클래스 필드에 함수를 할당
getName = function () {
return this.name;
}
}
const me = new Person();
console.log(me); // Person {name: 'Lee', getName: f}
console.log(me.getName()); // Lee
class Person {
// private 필드 정의
#name = '';
constructor(name) {
// private 필드 참조
this.#name = name;
}
}
const me = new Person('Lee');
// private 필드 #name은 클래스 외부에서 참조할 수 없다
console.log(me.#name); // SyntaxError
class Person {
// private 필드 정의
#name = '';
constructor(name) {
// private 필드 참조
this.#name = name;
}
get name() {
// private 필드를 참조하여 trim한 다음 반환한다.
return this.#name.trim();
}
}
const me = new Person('Lee');
console.log(me.name); // Lee
class MyMath {
// static public 필드 정의
static PI = 22 / 7;
// static private 필드 정의
static #num = 10;
// static 메서드
static increment() {
return ++MyMath.#num;
}
}
console.log(MyMath.PI); // 3.1425...
console.log(MyMath.increment()); // 11
class Animal {
constructor(age, weight) {
this.age = age;
this.weight = weight;
}
eat() {return 'eat';}
move() {return 'move';}
}
// 상속을 통해 Animal 클래스를 확장한 Bird 클래스
class Bird extends Animal {
fly() {return 'fly';}
}
const bird = new Bird(1, 5);
console.log(bird); // Bird {age: 1, weight: 5}
console.log(bird.eat()); // eat;
console.log(bird.move()); // move;
console.log(bird.fly()); // fly;
// 수퍼 클래스
class Base {}
// 서브 클래스
class Derived extends Base {}
// 생성자 함수
function Base(a) {
this.a = a;
}
class Derived extends Base {}
const derived = new Derived(1);
console.log(derived); // Derived {a: 1}
function Base1() {}
class Base2 {}
let condition = true;
class Derived extends (condition ? Base1 : Base2) {}
const derived = new Derived();
console.log(derived); // Derived {}
// 서브클래스에 생략시 암묵적 정의
constructor(...args) {super(...args);}
super 호출
class Base {
constructor(a, b) {
this.a = a;
this.b = b;
}
}
// 서브 클래스
class Derived extends Base {
// constructor(...args) {super(...args);}
}
const derived = new Derived(1,2);
console.log(derived); // Derived {a: 1, b: 2}
class Base {
constructor(a, b) {
this.a = a;
this.b = b;
}
}
// 서브 클래스
class Derived extends Base {
constructor(a, b, c) {
super(a, b);
this.c = c;
}
}
const derived = new Derived(1,2,3);
console.log(derived); // Derived {a: 1, b: 2, c: 3}
super 호출시 주의사항
1. 서브 클래스에서 constructor를 생략하지 않는 경우 서브클래스의 constructor에서는 반드시 super를 호출해야 한다.
2. 서브 클래스의 constructor에서 super를 호출하기 전에는 this를 참조할 수 없다.
3. super는 반드시 서브클래스의 constructor에서만 호출한다. 서브클래스가 아닌 클래스의 constructor나 함수에서 super를 호출하면 에러가 발생한다.
super 참조
class Base {
constructor(name) {
this.name = name;
}
sayHi() {
return `Hi! ${this.name}`;
}
static sayHi2() {
return `Hi2! ${this.name}`;
}
}
// 서브클래스
class Derived extends Base {
sayHi() {
return `${super.sayHi()}. how are you doing?`;
}
sayHi2() {
return `${super.sayHi2()}. how are you doing?`;
}
}
const derived = new Derived('Lee');
console.log(derived.sayHi()); // Hi! Lee. how are you doing?
console.log(derived.sayHi2()); // Hi2! Lee. how are you doing?
// 수퍼클래스
class Rectangle {
constructor(width, height) {
this.width = width;
this.height = height;
}
getArea() {
return this.width * this.height;
}
toString() {
return `width = ${this.width}, height = ${this.height}`;
}
}
// 서브클래스
class ColorRectangle extends Rectangle {
constructor(width, height, color) {
super(width, height);
this.color = color;
}
// 메서드 오버라이딩
toString() {
return super.toString() + `, color = ${this.color}`;
}
}
const colorRectangle = new ColorRectanle(2, 4, 'red');
// 상속을 통해 getArea 메서드 호출
console.log(colorRectangle.getArea()); // 8
// 오버라이딩된 toString 메서드 호출
console.log(colorRectangle.toString()); // width = 2, height = 4, color = red
1. 서브클래스의 super 호출
2. 수퍼 클래스의 인스턴스 생성과 this 바인딩
3. 수퍼클래스의 인스턴스 초기화
4. 서브클래스 constructor로 복귀와 this 바인딩
5. 서브클래스의 인스턴스 초기화
6. 인스턴스 반환
class MyArray extends Array {
// 중복된 배열 요소를 제거하고 반환한다.
uniq() {
return this.filter((v, i, self) => self.indexOf(v) === i);
}
// 모든 배열 요소의 평균을 구한다.
average() {
return this.reduce((pre, cur) => pre + cur, 0) / this.length;
}
}
const myArray = new MyArray(1,1,2,3);
console.log(myArray.uniq()); // [1, 2, 3]
console.log(myArray.average()); // 1.75
// 메서드 체이닝
console.log(myArray.filter(v = > v & 2).uniq().average()); //2