Class
- 클래스는 붕어빵을 만들 수 있는 틀을 말함. 템플릿만 정의해두고 어떤 데이터 형식이 들어올 수 있는지만 정의
- 실제로 메모리에 올라가지않음
Object
- 클래스를 이용하여 생성한 붕어빵을 말함. 실제로 데이터를 집어 넣음
- 실제로 메모리에 올라감
Javascript 에서 클래스를 정의방법
- introuduced in ES6 ( es6이전에는 클래스 없이 오브젝트를 만들 수 있었음)
- syntactical sugar over prototype-based inheritance (완전히 새롭게 추가된 것이 아니라, 기존에 존재하던 프로토타입에 기반하여 문법만 추가된 것 ==> syntactical sugar : 문법적으로 달달한..)
// 1. Class declarations class Person { //constructor constructor(name,age){ //fields this.name = name; this.age = age; } //methods speak() { console.log(`${this.name}: hello!`); } } const test = new Person('dylan',20); console.log(test.name); // dylan console.log(test.age); // 20 test.speak(); // dylan: hello!
Getter와 Setter를 쓰는 이유?
자판기 커피 머신을 예로 들면, class CoffeeMachine{} 내에 numberOfCoffee (int) property, putCoin(), makeCoffee() 등의 메서드가 있을 수 있다.
그럼 여기에서 numberOfCoffee 프로퍼티는 ‘-1’로 설정 될 수 있을까? 당연히 안된다. 그래서 Getter와 Setter를 쓰는 것이다.
사용자가 실수로 ‘-1’을 설정한다고 해도, 코드단에서 0으로 변경해주어야 한다. 그래서 Getter 와 Setter를 사용하는 것이다.
// 2. Getter and Setters class User { constructor(firstName, lastName, age){ this.firstName = firstName; this.lastName = lastName; this.age = age; //이때 getter & setter 호출. 'this.age'부분에서 getter , '= age' 에서 setter 호출 } get age() { return this._age; //age 변수명을 달리한다. 왜냐? 변수값이 다르지않으면 계속해서 호출이 일어남 } set age(value) { //if(value < 0) { // throw Error('age can not be negative!'); //} this._age = value < 0 ? 0 : value; //0보다 작으면 0으로 셋 } } const user1 = new User('Steve', 'Job', -1); // 유저가 실수로 나이를 -1로 세팅한 경우 ==> 이는 말이 안되므로 조금 더 방어적인 자세를 취해야함 console.log(user1.age); // 0
Encapsulation (캡슐화)
그럼 위의 커피머신 클래스에서 다른 사람이 numberOfCoffee 프로퍼티를 설정할 수 있게 하는게 좋을까 안좋을까? 자판기 커피 개수를 다른 사람이 수정하는 것은 당연히 안좋다. 그래서 numberOfCoffee 프로퍼티를 private으로 만드는 것이다. 이게 바로 Encapsulation(캡슐화)라고 볼 수 있다.
// 3. Fields (public, private) // 최근에 추가됨 class Experiment { publicField = 2; //생성자를 쓰지 않고 그냥 정의하면 public -> 외부에서 접근 가능 #privateField = 0; // #을 사용하여 private. 클래스 내부에서만 값이 보여지고 변경이 가능하고, 외부에서는 값을 확인하거나 변경할 수 없다 } const experiment = new Experiment(); console.log(experiment.publicField); // 2 console.log(experiment.privateField); // undefined
// 4. Static properties and methods // 최근에 추가됨 class Article() { static publisher = 'publisher'; constructor(articleNumber) { this.articleNumber = articleNumber; } static printPublisher() { console.log(Article.publisher); } } const article1 = new Article(1); const article2 = new Article(2); console.log(article1.publisher); // undefined (static은 오브젝트마다 할당되어 지는 것이 아닌, 클래스 자체에 붙어있음) console.log(Article.publisher); // publisher Article.printPublisher(); //publisher
클래스 내의 프로퍼티들과 메소드들은 오브젝트 생성시 그대로 복제되고 내부 데이터만 변경되는데, 오브젝트 데이터와 상관없이 동일하게 반복적으로 사용되어지는 메서드가 있을 수 있다. 그럴때 ‘static’을 사용
즉, 클래스로 들어오는 데이터에 상관없이 클래스에서 사용할 수 있는 것이라면 static을 사용하여 정의하는 것이 메모리의 사용을 줄여준다.
상속(Inheritance) & 다형성(Polymorphism)
도형을 그리는 어플리케이션을 개발한다고 가정해보자.
도형 클래스는 너비와 높이, 색상등의 프로퍼티와 색칠 할 수있는 메서드 등을 가질 수 있다. 여기서 너비와 높이는 항상 반복된다.
그래서 너비와 높이는 Shape 클래스에 정의해두고 다른 클래스를 만들때 Shape 클래스를 상속하여 field나 method를 그대로 확장해서 사용할 수 있다. (상속)
삼각형의 넓이의 경우에, 최종적으로 나누기 2를 해주어야 한다. Shape에서 정의된 메서드 getArea()는 width * height으로 정의되어 있으므로 상속이후 해당 메서드를 오버라이딩하여 다른 로직으로 바꿀 수 있고, 또 super을 이용하여 부모의 메서드를 호출하는 것도 가능하다. (다형성)
// 5. Inheritance // a way for one class to extend another class class Shape { constructor(width, height, color) { this.width = width; this.height = height; this.color = color; } // field draw() { console.log(`drawing ${this.color} color ! `); } // method getArea() { return this.width * this.height; } // method } class Rectangle extends Shape{} // 이렇게만 정의해도 Shape 클래스에서 정의한 field와 method들이 Rectangle 클래스에 포함됨 (상속) class Triangle extends Shape{ draw () { super.draw(); // 부모의 draw 메서드 호출 console.log('😀'); } // 다형성 getArea() { return (this.width * this.height) / 2; //override 다형성 } } const rectangle = new Rectangle(20, 20, 'blue'); rectangle.draw(); // drawing blue color console.log(rectangle.getArea()); // 400 const triangle = new Triangle(20,20,'red'); triangle.draw(); // drawing red color, 😀 두개 다 호출 console.log(triangle.getArea()); // 200
instanceOf
// 6. Class checking : instanceOf console.log(rectangle instanceOf Rectangle); // True console.log(triangle instanceOf Rectangle); // False console.log(triangle instanceOf Triangle); // True console.log(triangle instanceOf Shape); // True -> Shape를 상속했으므로 console.log(triangle instanceOf Object); // True -> 모든 오브젝트는 자바스크립트의 'Object'를 상속한다.
모든 오브젝트들은 자바 스크립트의 Object를 상속하기 때문에 Object내에 정의되어 있는 toString() , valueOf() 등의 메서드를 오버라이딩 하여 재정의할 수 있다.
class Triangle extends Shape{ draw () { super.draw(); console.log('😀'); } getArea() { return (this.width * this.height) / 2 ; //override 다형성 } toString() { return `Triangle: color: ${this.color}`; //Object 내의 메서드 override도 가능하다 } }