ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 자바스크립트 Chapter 14 Factories and Classes
    Computer Science/JavaScript 2019.05.06 21:53

    https://github.com/leonardomso/33-js-concepts

     

    자바스크립트 핵심 컨셉 33가지

     

    Chapter 14 Factories and Classes

     

    이번 장에서는 Factories와 Classes를 살펴보자.

     

    자바스크립트에서 팩토리 함수란,

     

    함수가 객체를 반환할 때를 의미한다.

     

    간단한 팩토리 함수의 예제

     

    function createDaniel() {
     return {
        name: 'daniel',
        height: '180cm',
        gender: 'male'
     }
    }

     

    아래의 팩토리 함수 예제는

     

    객체의 반환 값을 변경할 수 있는

     

    parameter와 함께 정의 가능하다.

     

    function createAny(gender='female') {
      return {
        type: 'any',
        height: 170,
        gender
      }
    }

     

    위의 Daniel과 Any의 조합도 가능하다.

     

    function createPerson() {
      return {
        name: 'person',
        people: [
          createDaniel(),
          createAny()
        ]
      }
    }

     

    프로미스를 대신 반환하는 비동기 팩토리 함수

     

    function getFood(foodUrl) {
      return new Promise((resolve, reject) => {
        fetch(foodurl)
          .then(result => {
            resolve({
              type: 'food',
              courses: result.json()
           })
         })
         .catch(reject);
      })
    }

     

    위의 중첩 코드를 여러 개의 구분된 팩토리 함수들로

     

    복잡한 중첩 코드 깨뜨리기

     

    function foodUrl(foodUrl) {
      return fetch(foodUrl)
        .then(result => result.json())
        .then(json => createFood(json))
    }
    
    function createFood(courses=[]) {
      return {
        type: 'food',
        courses
      }
    }

     

    프로미스 객체들을 반환하는 팩토리 함수를 조합하기 위한

     

    Promise.all 사용하기

     

    function getWeeksFoods() {
      const foodUrl = 'foodurl'
      
      return Promise.all([
        getFood(`${foodUrl}/monday`),
        getFood(`${foodUrl}/tuesday`),    
        getFood(`${foodUrl}/wednesday`),    
        getFood(`${foodUrl}/thursday`),    
        getFood(`${foodUrl}/friday`)
      ])
    }

     

    고차 팩토리 함수

     

    함수를 인자로 받거나 함수를 반환하는 함수이다.

     

    function giveTimestamp(factory) {
      return (...args) => {
        const instance = factory(...args);
        const time = Date.now();
        return { time, instance };
      };
    }
    
    const createOrder = giveTimestamp(function(ingredients) {
      return {
        type: 'order',
        ingredients
      };
    });

     

    불변 객체를 반환하는 팩토리 함수를 보장하려면

     

    Object.freeze() 메서드를 이용해보자.

     

    (freeze는 객체를 동결시킴)

     

    function freezer(factory) {
      return (...args) => Object.freeze(factory(...args)));
    }
    
    const createImmutableIceCream = freezer(createIceCream);
    
    createImmutableIceCream('strawberry').flavour = 'mint'; // Error!

     

    이번에는 클래스를 한번 살펴보자.

     

    자바스크립트에서 클래스(Class)는 사실 함수의 유형이다.

     

    단지 "문법적 설탕(syntactical sugar)"이 가미된 것이다.

     

    클래스를 이해하려면 생성자 함수(Constructor Functions)

     

    그리고 Prototype에 대해 알아야 한다.

     

    생성자 함수(Constructor Functions)

     

    function Vehicle(make, model, color) {
            this.make = make,
            this.model = model,
            this.color = color,
            this.getName = function () {
                return this.make + " " + this.model;
            }
    }

     

    위의 함수는 Java 클래스와 거의 동일한 기능을 제공한다.

     

    let car = new Vehicle("Toyota", "Corolla", "Black");
    let car2 = new Vehicle("Honda", "Civic", "White");

     

    Vehicle 유형의 객체를 여러 개 만들 수 있다.

     

    그러나 문제가 다소 있다.

     

    자바스크립트 엔진은 두 객체의 각각에 대한

     

    Vehicle 생성자 함수의 사본을 만든다.

     

    모든 속성과 메서드는 Vehicle의 새 인스턴스에 복사된다.

     

    여기서 문제는 생성자 함수의 멤버 함수(메서드)가

     

    모든 객체에서 불필요하게 반복될 필요가 없고

     

    기존 객체에 새 속성이나 메서드를 추가할 수 없다는 점이다.

     

    car2.year = '2012'

     

    year 속성(property)을 추가하려면 해당 속성을

     

    생성자 함수 자체에 추가해야 한다.

     

    function Vehicle(make, model, color, year) {
            this.make = make,
            this.model = model,
            this.color = color,
            this.year = year,
            this.getName = function () {
                return this.make + " " + this.model;
            }
    }

     

    프로토타입(Prototype)

     

    함수가 생성될 때 자바스크립트 엔진은

     

    prototype 속성(property)을 추가한다.

     

    이 속성은 객체이며 prototype object라고 한다.

     

    프로토타입 객체에는 함수를 가리키는

     

    constructor 속성이 있고

     

    객체인 __proto__ 속성은 생성자 함수의

     

    prototype 속성(property)을 가리킨다.

     

    생성자 함수의 새 인스턴스가 만들어질 때마다

     

    이 속성은 다른 속성 및 메서드와 함께 인스턴스에 복사된다.

     

    프로토타입 객체는 생성자 함수에 새 속성 및 메서드를 추가하는데

     

    사용할 수 있으며 생성자 함수의 모든 인스턴스에서 사용할 수 있다.

     

    car.prototype.year = '2016'

     

    프로토타입 접근법을 사용할 때 주의할 점도 있다.

     

    프로토타입 속성과 메서드는 모든 인스턴스 간에 공유되지만

     

    생성자 함수의 인스턴스 중 하나가 기본 속성에서 변경되면

     

    모든 인스턴스가 아닌 해당 인스턴스에만 반영된다.

     

    function Vehicle(make, model, color) {
            this.make = make,
            this.model = model,
            this.color = color,
            this.getName = function () {
                return this.make + " " + this.model;
            }
    }
    
    const pride = new Vehicle('KIA', 'Pride', 'Black')
    
    Vehicle.prototype.year = '2014'
    
    console.log(pride) 
    // Vehicle {make: 'KIA', model: 'Pride', color: 'Black', getName: [Function] }
    
    const suv = new Vehicle('KIA', 'Suv', 'white')
    
    console.log(suv)
    // Vehicle { make: 'KIA', model: 'Suv', color: 'white', getName: [Function] }
    
    suv.year = '2016' // '2016'
    
    pride.year // '2014'

     

    또 다른 하나는

     

    참조 유형 속성이 모든 인스턴스 간에 항상 공유된다는 점이다.

     

    예를 들면

     

    생성자 함수의 한 인스턴스가 수정하면 모든 인스턴스에 대해 수정된다.

     

    function Vehicle(make, model, color) {
            this.make = make,
            this.model = model,
            this.color = color,
            this.getName = function () {
                return this.make + " " + this.model;
            }
    }
    
    Vehicle.prototype.availableColors = ['Red', 'Yellow', 'Blue']
    
    const pride = new Vehicle('KIA', 'Pride', 'Black')
    
    const suv = new Vehicle('KIA', 'Suv', 'white')
    
    suv.availableColors.push('Black')
    
    suv.availableColors // ['Red', 'Yellow', 'Blue', 'Black']
    
    pride.availableColors // ['Red', 'Yellow', 'Blue', 'Black']

     

    Prototype 쉽게 이해하기

     

    Prototype Link + Prototype Object -> Prototype



    객체는 언제나 함수로 생성된다.



    자바스크립트에서 기본적으로 제공하는 Object는 함수다.



    Object와 마찬가지로 Function, Array도 모두 함수로 정의되어 있다.



    함수가 정의될 때 2가지 일이 동시에 이루어진다.



    1. 해당 함수에 Constructor(생성자) 자격 부여



    Constructor 자격이 부여되면 new를 통해 객체를 만들어 낼 수 있게 된다.



    이것이 함수만 new키워드를 사용할 수 있는 이유이다.



    2. 해당 함수의 Prototype Object 생성 및 연결



    Prototype Object도 같이 생성된다.



    생성된 함수는 prototype이라는 속성을 통해 Prototype Object에 접근할 수 있다.



    Prototype Object는 일반적인 객체와 같으며 

     

    기본적인 속성으로 constructor, __proto__ 가지고 있다.



    constructor는 Prototype Object와 같이 생성되었던 함수를 가리키고 있음.



    Prototype Object는 일반적인 객체이므로 속성을 마음대로 추가/삭제할 수 있다.



    __proto__는 Prototype Link이다.



    __proto__ 속성은 모든 객체가 빠짐없이 가지고 있는 속성이다.



    __proto__는 객체가 생성될 때 조상이었던 함수의 Prototype Object를 가리킨다.



    __proto__ 속성을 통해 상위 프로토타입과 연결되어 있는 형태를 프로토타입 체인이라고 한다.



    이런 프로토타입 체인 구조 때문에 모든 객체는 Object의 자식이라고 불리고,



    Object Prototype Object에 있는 모든 속성을 사용할 수 있다.

     

    생성자 함수 그리고 프로토타입(prototype)에 대해 알았으니

     

    클래스를 살펴보자.

     

    클래스는 프로로타입의 기능을 활용하여 생성자 함수를

     

    작성하는 새로운 방법일 뿐이다.

     

    class Vehicle {
      constructor(make, model, color) {
      	this.make = make;
            this.model = model;
            this.color = color;
          }
          
          getName() {
          	  return this.make + ' ' + this.model;
          }
    }

     

    클래스 Vehicle의 새로운 인스턴스를 생성하기 위해서는

     

    아래와 같다.

     

    let car = new Vehicle('KIA', 'pride', 'Black');

     

    생성자 함수와 비교해보면 거의 유사하다.

     

    클래스에 정의 된 함수 생성자(constructor)를 참조하는

     

    Vehicle이라는 변수를 만들었고,

     

    Vehicle의 프로토타입에 메서드를 추가했다.

     

    function Vehicle(make, model, color) {
        this.make = make;
        this.model = model;
        this.color = color;
    }
    
    Vehicle.prototype.getName()= function () {
        return this.make + ' ' + this.model;
    }
    
    let car = new Vehicle('KIA', 'pride', 'Black');

     

    클래스는 생성자 함수를 수행하는 새로운 방법일 뿐이다.

     

    또한 실제 클래스 같게 하기 위한 규칙도 있다.

     

    1. 생성자(constructor)가 작동하려면 new 키워드가 필요하다.

     

    아래와 같이 생성자(constructor)가 호출된다는 것을 의미한다.

     

    let car = new Vehicle('KIA', 'pride', 'Black');

     

    그리고 이것은 생성자 함수에서도 할 수 있다.

     

    function Vehicle(make, model, color) {
        this.make = make;
        this.model = model;
        this.color = color;
    }
    
    let car = Vehicle('KIA', 'pride', 'Black');

     

    2. 클래스 메서드는 열거되지(non-enumberable) 않는다.

     

    자바스크립트에서 객체의 각 속성(property)에는

     

    해당 속성에 대해 수행할 일부 작업에 대한

     

    열거 가능 플래그(enumberable flag)가 있다.

     

    클래스는 프로토타입에 정의된 모든 메서드에 대해

     

    플래그(flag)를 false로 설정한다

     

    3. 클래스에 생성자(constructor)를 추가하지 않으면,

     

    빈 생성자가 자동으로 추가된다.

     

    constructor() {}

     

    4. 클래스 안의 코드는 항상 엄격(strict) 모드이다.

     

    5. 클래스 선언은 호이스팅 되지 않는다.

     

    여기서 호이스팅이란?

     

    모든 선언이 현재 스코프 위에 자동으로 이동하는 동작이다.

     

    스코프란?

     

    2019/04/22 - [Computer Science/JavaScript] - 자바스크립트 Chapter 6 Scope

     

    정리하자면 클래스 선언은 선언되기 전에

     

    클래스를 사용할 수 없다는 걸 의미한다.

     

    6. 클래스는 생성자 함수나 객체 리터럴과 같은

     

    속성 값 할당을 허용하지 않는다.

     

    함수 또는 getters / setters 만 가질 수 있다.

     

    따라서 property:value 없다. 클래스에서 직접 값을 할당해야 한다.

     

    클래스의 특징을 조금 더 살펴보자.

     

    1. Constructor

     

    생성자는 클래스 자체를 나타내는 함수를 정의하는

     

    클래스 선언의 특수 함수이다.

     

    클래스 인스턴스를 새로 생성하면 생성자가 자동으로 호출된다.

     

    let car = new Vehicle('KIA', 'pride', 'Yellow');

     

    생성자는 super 키워드를 사용하여 확장된 클래스의 생성자를

     

    호출할 수 있다.

     

    2. Static Methods

     

    Static Methods는 프로토타입에 정의된 클래스의 다른 메서드와 달리

     

    프로토타입이 아닌 클래스 자체의 함수이다.

     

    주로 utility functions을 만들 때 사용된다.

     

    클래스의 인스턴스를 만들지 않고 호출된다.

     

    class Vehicle {
        constructor(make, model, color) {
            this.make = make;
            this.model = model;
            this.color = color;
        }
    
        getName() {
            return this.make + " " + this.model;
        }
        
        static getColor(v) {
            return v.color
        }
    }
    
    let car = new Vehicle('KIA', 'pride', 'Red');
    
    Vehicle.getColor(car); // 'Red'
        	
    

     

    3. Getters / Setters

     

    클래스는 또한 getter/setters 사용하여

     

    속성 값을 가져오거나 설정할 수 있다.

     

    class Vehicle {
    	constructor(model) {
        	this.model = model;
        }
        
        get model() {
    		return this._model;
        }
        
        set model() {
        	this._model = value;
        }
    }

     

    getters/setters는 클래스 프로토타입에 정의된다.

     

     

    4. Subclassing

     

    하위 클래스(Subclassing)는 자바스크립트 클래스 상속을

     

    구현할 수 있는 방법이며 키워드 확장은 클래스의 하위 클래스를

     

    생성하는 데 사용된다.

     

    class Vehicle {
        constructor(make, model, color) {
            this.make = make;
            this.model = model;
            this.color = color;
        }
    
        getName() {
            return this.make + ' ' + this.model;
        }
    }
    
    class Car extends Vehicle {
    	getName() {
        	return this.make + ' ' + this.model + ' in child class.';
        }
    }
    
    let car = new Car('KIA', 'pride', 'Red');
    
    car.getName(); // 'KIA pride in child class.'

     

    getName을 호출했을 때, 하위 클래스의 함수가 호출된 것을 볼 수 있다.

     

    때로는 기본 클래스의 함수를 호출해야 하는데,

     

    하위 클래스의 메서드 내에서 기본 클래스의 메서드를

     

    호출하기 위해 super 키워드를 사용한다.

     

    class Car extends Vehicle {
    	getName() {
        	return super.getName() + ' called base class function from child class. ';
         }
    }

     

    다시 한번 호출해보자.

     

    class Vehicle {
        constructor(make, model, color) {
            this.make = make;
            this.model = model;
            this.color = color;
        }
    
        getName() {
            return this.make + ' ' + this.model;
        }
    }
    
    class Car extends Vehicle {
    	getName() {
        	return super.getName() + ' called base class function from child class. ';
        }
    }
    
    let car = new Car('KIA', 'pride', 'Red');
    
    car.getName(); // 'KIA pride called base class function from child class. '

     

    이번 주제는 스크립트 언어라고 만만하게 봐서는 안되고 

     

    주제에서 다루는 내용 하나하나가 정말 중요하다는 걸 느꼈다.

     

    여러 아티클을 많이 읽어보았지만

     

    깔끔하고 시원하고 명확하게

     

    이해가 되지 않는다.

     

    Chapter가 올라갈수록 난이도도 점점 올라가는 느낌이다.

    댓글 0