class
- template
- declare once
- no data in

붕어빵 틀!
한번만 선언해서 사용.
object
- instance of a class
- created many times
- data in

붕어빵 틀을 이용해서 만든 팥붕어빵!

'use strict'
//Object oriented programming
//Javascript classes
// - introducing in ES6
// - syntactical sugar over prototype-based inheritance

// 1. Class declaration
class Person {
    //constructor
    constructor(name, age) {
        //fields
        this.name = name;
        this.age = age;
    }

    //methos
    speak() {
        console.log(`${this.name}: hello!`);
    }
}

const ellie = new Person('malze', '20');
console.log(ellie.name);
console.log(ellie.age);
ellie.speak();

// 2. Getter and Setters
class User {
    constructor(firstName, lastName, age) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
        //get age()를 정의하는 순간 this.age 는 아래의 get age() 를 호출한다.
        //set age(value) 를 정의하는 순간 = age; 는 set age(value) 를 호출한다.
    }
    
    get age() {
        return this._age;
    }

    set age(value) {
        // if(value < 0) {
        //     throw Error('age can not be negative');
        // }
         this._age = value < 0 ? 0 : value;
    }
    
}

const user1 = new User('Steve', 'Job', -1);
console.log(user1._age);

// 3. Fields (public, private)
class Experient {
    publicField = 2;
    #privateField = 0;
}

const experient = new Experient();
console.log(experient.publicField); // 2
console.log(experient.privateField); // undefined

// 4. Static properties and methods
class Article {
    static publisher = 'Dream Coding';
    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
article1.printPublisher(); // error!
console.log(Article.publisher); // class 자체에만 연결되어있다.
Article.printPublisher();

// 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;
    }

    draw() {
        console.log(`drawing ${this.color} color of`);
    }

    getArea() {
        return this.width * this.height;
    }
}

class Rectangle extends Shape {}
class Triangle extends Shape {
    draw() {
        super.draw();
        console.log('◎');
    }
    getArea() {
        return this.width * this.height / 2;
    }

    toString() {
        return `Triangle: color is ${this.color}`;
    }
}
const rectangle = new Rectangle(20, 20, 'blue');
rectangle.draw();
const triangle = new Triangle(10, 10, 'red');
triangle.draw();
console.log(triangle.getArea()); 

// 6. Class checking: instanceOf
// 왼쪽에 있는 object 가 오른쪽 class 의 instance 인지 판별
console.log(rectangle instanceof Rectangle);
console.log(triangle instanceof Rectangle);
console.log(triangle instanceof Triangle);
console.log(triangle instanceof Shape);
console.log(triangle instanceof Object); // js 에서 만든 모든 class 는 js 의 Object 를 상속한다.
console.log(triangle.toString());
class Counter {
    constructor(runEveryFiveTimes) {
        console.log(`constructor 실행`);
        this.counter = 0;
        this.callback = runEveryFiveTimes;
    }
//class 함수를 선언할 때는 function 생략 가능
    increase() {
        this.counter++;
        console.log(this.counter);
        if(this.counter % 5 === 0) {
            this.callback && this.callback(this.counter); // callback 이 제대로 할당 되어야만
        }
    }
}

function printSomething(num) {
    console.log(`wow! ${num}`);
}

function alertNum(num) {
    alert(`wow! ${num}`);
}

const coolCounter = new Counter(printSomething);
const alertCounter = new Counter(alertNum);
coolCounter.increase();
coolCounter.increase();
coolCounter.increase();
coolCounter.increase();
coolCounter.increase();
alertCounter.increase();
alertCounter.increase();
alertCounter.increase();
alertCounter.increase();
alertCounter.increase();