ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 자바스크립트 객체지향 기본기
    코드잇 부스트 2024. 6. 3. 23:26

    자바스크립트 객체지향 기본기

    1. 객체와 클래스

    객체 지향 프로그래밍이란?

     구성:

    • 프로퍼티(property): 변수
    • 메소드(method): 함수

    *절차 지향 프로그래밍(Process-Oriented Programming, POP):

        데이터 구조와 이에 수반하는 동작들을 분리하는 컴퓨터 프로그래밍의 패러다임

     

     

    객체 만들기 1-1: Object-Literal

    const user = {
      email: 'chris123@google.com',
      birthdate: '1992-03-21',
      buy(item) {
        console.log(`${this.email} buys ${item.name}`);
      },
    };
    
    • 중괄호를 쓰고 그 안에 프로퍼티와 메소드를 나열하는 것

     

    객체 만들기 1-2: Factory function

    function createUser(email, birthdate) {
      const user = {
        email,
        birthdate,
        buy(item) {
          console.log(`${this.email} buys ${item.name}`);
        },
      };
      return user;
    }
    
    const user1 = createUser('a@email.com', '1999-09-01');
    const user2 = createUser('b@email.com', '1998-07-02');
    const user3 = createUser('c@email.com', '1990-11-24');
    
    • 객체를 생성해서 리턴하는 함수

     

    객체 만들기 2: Constructor function

    • constructor function 내에서 생성할 객체를 가리킨다.
    • new 키워드를 앞에 붙여서 함수를 호출해야 정상적으로 작동한다.
    • 리턴문을 작성하지 않아도 생성한 객체를 자동으로 리턴한다.
    • 함수의 첫 문자를 대문자로 지정하는 것을 권장한다.
    function User(email, birthdate) 
    {	
      this.email = email; 
      this.birthdate = birthdate; 
      this.buy = function (item) 
      { 
        console.log(`${this.email} buys ${item.name}`); 
      }; 
    } 
    
    const user1 = new User('a@email.com', '1999-09-01'); 
    const user2 = new User('b@email.com', '1998-07-02'); 
    const user3 = new User('c@email.com', '1990-11-24');
    • 함수 안에서 this를 활용해 객체를 생성하고 리턴하는 특별한 함수

     

    객체 만들기 3: Class(ES2015)

    class를 생성하기 위한 템플릿

    • this 키워드는 Class 내에서 생성할 객체를 가리킨다.
    • 프로퍼티는 constructor() 안, 메소드나 getter는 constructor() 바깥에 작성한다.
    • 세 가지 방법(Declaration, unnamed expression, named expression)으로 Class 객체를 만들 수 있다.
    • Hoisting을 허용하지 않는다.
    // Class 선언식(declaration)
    class User1 {
      constructor(email, birthdate) {
        this.email = email;
        this.birthdate = birthdate;
      }
      buy(item) {
        console.log(`${this.email} buys ${item.name}`);
      }
    }
    
    const user1 =  new User('a@email.com', '1999-09-01');
    // Class 표현식(expression)
    // unnamed
    const User = class {
      constructor(email, birthdate) {
        this.email = email;
        this.birthdate = birthdate;
      }
      buy(item) {
        console.log(`${this.email} buys ${item.name}`);
      }
    }
    
    const user1 =  new User('a@email.com', '1999-09-01');
    
    // Class 표현식(expression)
    // named
    const User = class UserClassName {
      constructor(email, birthdate) {
        this.email = email;
        this.birthdate = birthdate;
      }
      buy(item) {
        console.log(`${this.email} buys ${item.name}`);
      }
    }
    
    const user1 = new User('a@email.com', '1999-09-01');
    
    User.name // "UserClassName"
    

     


    2. 객체 지향 프로그래밍의 4개의 기둥

    추상화(abstraction)

    • 어떤 구체적인 것을 원하는 방향으로 간략화해서 나타내는 것.
    • (기능을 명시하고 구현은 생략한다.)

     

    캡슐화(Encapsulation)

    외부에서 객체의 특정 프로퍼티에 직접 접근하지 못하도록 막는 것.

    특정 프로퍼티에 대한 접근은 getter, setter 메소드를 통해서만 가능하다.

    class User 
    { 
      constructor(email, birthdate) { 
        this.email = email; 
        this.birthdate = birthdate; 
      } 
      buy(item) { 
        console.log(`${this.email} buys ${item.name}`); 
      } 
      get email() { 
        return this._email; 
      } 
      set email(address) { 
        if (addrress.includes('@')) { 
          this._email = address; 
        } else { 
          throw new Error('invalid email address'); 
        } 
      } 
    }
    • get()는 obj.propName을 사용해 프로퍼티를 읽으려고 할 때 실행된다.
    • set()는 obj.propName = value로 프로퍼티에 값을 할당하려 할 때 실행된다.
    • 프로퍼티 네임과 동일한 이름을 getter, setter의 이름으로 사용했기 때문에, 프로퍼티에 접근하려면 프로퍼티 네임 앞에 언더바(_)를 붙여 접근한다.
    • getter, setter만 설정할 경우 obj._propName의 값은 여전히 접근/수정이 가능하기 때문에 완벽한 캡슐화라고 볼 수 없다.

     

    캡슐화 더 알아보기

    Java에는 private이라는 키워드가 있어서 문법 차원에서 캡슐화를 지원하지만, JavaScript에는 캡슐화를 자체적으로 지원하는 문법이 아직 없다.

    *클로저(Closure)라고 하는 개념을 응용해서 완벽한 캡슐화를 할 수 있다.

    • closure 어떤 함수와 그 함수가 참조할 수 있는 값들로 이루어진 환경을 하나로 묶어서, 함수가 정의된 당시에 참조할 수 있었던 변수들을 계속 참조할 수 있는 상태의 함수
    function createUser(email, birthdate) {
      let _email = email;
    
      const user = {
        birthdate,
    
        get email() {
          return _email;
        },
    
        set email(address) {
          if (address.includes('@')) {
            _email = address;
          } else {
            throw new Error('invalid email address');
          }
        },
      };
    
      return user;
    }
    • 위와 같이 function 안, user 객체 바깥에 _email 변수를 만들고, _email 변수의 값을 읽고 쓸 수 있는 email이라는 getter/setter 메소드를 만들면 완벽한 캡슐화가 가능하다.
    • 메소드에도 closure 개념을 응용할 수 있다
    function createUser(email, birthdate) {
      const _email = email;
      let _point = 0;
    
      // 사용자가 물건을 살 때마다 1포인트씩 적립해 주는 함수
      function increasePoint() {
        _point += 1;
      }
    
      const user = {
        birthdate,
    
        get email() {
          return _email;
        },
    
        get point() {
          return _point;
        },
    
        buy(item) {
          console.log(`${this.email} buys ${item.name}`);
          increasePoint();
        },
      };
    
      return user;
    }
    • function 안, user 객체 바깥에 _point 변수와 incresePoint() 함수를 만들고, _point 변수의 값을 읽을 수 있는 point라는 getter 메소드를 만들면 완벽한 캡슐화가 가능하다.

     

    상속(inheritance)

    자식 객체가 부모 객체의 프로퍼티와 메소드를 물려받는 것

    • extends 클래스를 다른 클래스의 자식으로 만들기 위해 사용되는 키워드
    class User {
      constructor(email, birthdate) {
        this.email = email;
        this.birthdate = birthdate;
      }
      buy(item) {
        console.log(`${this.email} buys ${item.name}`);
      }
    }
    
    class PremiumUser extends User {
      constructor(email, birthdate, level) {
        super(email, birthdate);
        this.level = level;
      }
    
      streamMusicForFree() {
        console.log(`Free music streaming for ${this.email}`);
      }
    }
    • 자식 클래스의 constructor() 안쪽에 super() 키워드를 호출해야 자식 클래스에서 this 키워드에 접근할 수 있다. 호출하기 전에는 ReferenceError가 발생한다.

     

    다형성(Polymorphism)

    프로그램 언어 각 요소들이 다양한 데이터 타입에 속하는 것이 허가되는 성질

    const user1 = new User('chris123@google.com', '19920321');
    const user2 = new User('rache@google.com', '19880516');
    const user3 = new User('brian@google.com', '20051125');
    
    const pUser1 = new PremiumUser('tommy@google.com', '19901207', 3);
    const pUser2 = new PremiumUser('helloMike@google.com', '19900915', 2);
    const pUser3 = new PremiumUser('alicekim@google.com', '20010722', 5);
    
    const users = [user1, pUser1, user2, pUser2, user3, pUser3];
    
    users.forEach((user) => {
      user.buy(item);
    });
    

    하나의 변수인 user가 다양한 종류의 객체 User, PremiumUser를 가리킬 수 있는 것도 다형성이라고 한다.

    • *자식 클래스에서 부모 클래스의 메소드를 오버라이딩하고 그 후에 이 다형성을 활용하는 경우가 많다.

     

    부모 클래스의 메소드가 필요하다면?

    • 자식 클래스에서 부모 클래스의 메소드를 오버라이딩해서 부모 클래스의 메소드를 불러올 수 없을 때, super.method() 키워드를 사용해서 부모 클래스의 메소드를 자식 클래스 내에서 불러올 수 있다.

     

    instanceof 연산자

    object instance of constructor:

    • constructor의 프로토타입 프로퍼티가 객체의 프로토타입 체인에 나타나는지 여부를 검사하는 연산자
    • 객체의 프로토타입 체인을 검사하므로 자식 클래스로 만든 객체를 왼쪽에, 부모 클래스를 오른쪽에 넣고 instaceof 연산자를 적용해도 true를 리턴한다.

     

    static 프로퍼티와 static 메소드

    static 클래스로 만든 객체가 아니라 클래스 자체로 접근할 수 있는 프로퍼티나 메소드

    class Math {
      static PI = 3.14;
    
      static getCircleArea(radius) {
        return Math.PI * radius * radius;
      }
    }
    
    // static 프로퍼티 값 변경
    Math.PI = 3.141592;
    // static 메소드 추가
    Math.getRectangleArea = function (width, height) {
      return width * height;
    }
    
    console.log(Math.PI);
    console.log(Math.getRectangleArea(4, 5));
    
    // Date 내장 객체의 static 메소드 now()
    console.log(Date.now());
    • static property나 method도 getter, setter 메소드로 캡슐화할 수 있다.

     

    클래스는 파일 하나당 하나씩 넣어주는 게 좋아요

    보통 하나의 클래스를 모듈화하여 export하는 방식을 많이 사용한다

Designed by Tistory.