Unit2 회고

YEN J·2022년 9월 22일
0

code states

목록 보기
18/43

객체지향

클로저 모듈 패턴

  • 메서드 호출: 객체 내 메서드를 호출하는 방법
  • 클래스 모듈 패턴 -> 클로저를 이용하여 일부 요소를 다른 요소와 독립적으로 유지하는 패턴을 구현한 것
    let counter = (function() {
      let privateCounter = 0;
      function changeBy(val) {
        privateCounter += val;
      }
      return {
        increment: function() {
          changeBy(1);
        },
        decrement: function() {
          changeBy(-1);
        },
        value: function() {
          return privateCounter;
        }
      };
    })();

    console.log(counter.value()); // 0
    counter.increment();
    counter.increment();
    console.log(counter.value()); // 2
    counter.decrement();
    console.log(counter.value()); // 1
  • counter.increment, counter.decrement, counter.value는 하나의 어휘적 환경(lexical environment) 공유
    • 위 함수와 변수는 리턴값으로 공개될 멤버
  • 이 환경은 2개의 은닉될 멤버인 변수privateCounter와 함수changeBy 포함
    • 은닉될 멤버는 함수 외부에서 접근 불가능
    • 함수에서 반환된 공개될 멤버를 통해서만 접근 가능(lexical scope를 통해)
  • 이 예시는 단순 객체를 사용 -> 재사용성 낮음
    let makeCounter = function() {
      let privateCounter = 0;
      function changeBy(val) {
        privateCounter += val;
      }
      return {
        increment: function() {
          changeBy(1);
        },
        decrement: function() {
          changeBy(-1);
        },
        value: function() {
          return privateCounter;
        }
      }
    };

    let counter1 = makeCounter();
    let counter2 = makeCounter();
    console.log(counter1.value()); // 0
    counter1.increment();
    counter1.increment();
    console.log(counter1.value()); // 2
    counter1.decrement();
    console.log(counter1.value()); // 1
    console.log(counter2.value()); // 0
  • 위 함수를 makeCounter라는 변수에 할당 시 이 변수를 통해 여러 개의 카운터 생성 가능
  • 생성된 각 카운터는 고유의 클로저를 통해 은닉될 멤버에 접근
    • 각각의 클로저는 다른 클로저의 값에 영향을 주지 않음(독립성 유지)
  • 클로저를 통해 객체 지향 프로그래밍의 정보 은닉과 캡슐화 구현
  • 이 예시는 클로저 모듈 패턴을 이용 -> 재사용성 높음
    클로저-모듈패턴

클래스와 인스턴스

객체 지향 프로그래밍이란?
하나의 모델이 되는 청사진(설계도)을 만들고(class), 이를 바탕으로 한 객체(object)를 만드는(instance) 프로그래밍 패턴

  • 클래스를 만드는 방법
    • class의 경우 일반적인 함수 정의하는 것과 비슷하게 만듦
      • 다른 함수와의 구분을 위해 대문자로 시작, 일반명사로 만듦
      • ES6에서 class 키워드 도입(주로 사용)
    • constructor -> 인스턴스가 초기화될 때 실행되는 생성자 함수, return 값 x
  • 인스턴스를 만드는 방법
    • 함수 이용 시(새 인스턴스를 만들 경우) new 키워드 활용
    • 즉시 생성자 함수(constructor) 실행, 변수에 인스턴스 할당
    • 각각의 인스턴스는 클래스의 고유한 속성과 메서드를 가짐
  • 속성과 메소드
    • 클래스에 속성과 메소드 정의, 인스턴스에서 이용

    • 클래스: 속성의 정의

      • this 키워드 활용 -> this: 함수 실행 시, 해당 scope 마다 생성되는 고유한 실행 context로 new 키워드로 생성 시 해당 인스턴스가 this의 값이 됨
      • this에 할당한다는 것은 인스턴스에 해당 속성을 부여하겠다는 의미
    • 클래스: 메서드의 정의

      • ES5에서는 prototype 키워드를 사용
      • prototype: 모델의 청사진을 만들 때 쓰는 원형 객체
      function Student(name, age, gender){} // 속성 정의
       Student.prototype.study = function() {} // 메서드 정의
      • ES6에서는 생성자 함수와 함께 키워드 안쪽에 묶어서 정의
      class Student {
          constructor(name, age, gender){} // 속성 정의
          study(){} // 메서드 정의
      }
      class Car {
          constructor(brand, name, color){
              this.brand = brand;
              this.name = name;
              this.color = color;
          }
          drive() {
              console.log(this.name + '가 운전을 시작합니다');
          }
      } // ES6 방식으로 작성해 본 Car 클래스

객체 지향 프로그래밍

객체 지향의 특징

  • 코드를 추상화 -> 직관적 이해 가능
  • 속성과 메서드(데이터와 기능)를 묶어서 처리 -> 하나의 객체라는 개념에 포함, 클래스(Class)라고 부름
    • JavaScript는 객체 지향 언어이다(x)
    • JavaScript는 객체 지향 패턴으로 작성할 수 있다(o)

객체 지향 프로그래밍(OOP)

  • 프로그램 설계 철학 중 하나
  • 객체(속성과 메서드가 존재)로 그룹화
    • 객체 생성 시 메모리 상에서 반환되기 전까지 객체(속성, 메서드) 유지
  • 4가지 주요 개념을 통해 재사용성 획득
    • 캡슐화, 상속, 추상화, 다형성

클래스와 인스턴스

  • 클래스: 속성이 들어가지 않은 청사진
    • 객체를 만들기 위한 생성자 함수 포함
    • 생성자를 통해 속성을 넣음
  • 인스턴스: 클래스를 통해 만들어진 객체

OOP의 4가지 주요 개념

  • Encapsulation(캡슐화)
    • 데이터와 기능(속성과 메서드)을 하나의 객체 안에 넣어 묶는 것
    • 느슨한 결합(Loose Coupling)에 유리: 원할 때 구현 수정 가능
    • 캡슐화는 “은닉화”의 특징도 포함
      • 은닉화: 내부 데이터나 내부 구현이 외부로 노출되지 않도록 하는 것
        -> 데이터나 구현은 숨기고, 메서드(동작)만 노출시킴
  • Abstraction(추상화)
    • 복잡한 내부 구현과 달리 인터페이스 단순화를 구현하는 것
    • 예기치 못한 변화의 영향을 줄임
    • 캡슐화는 데이터의 ‘은닉’에 focusing, 상속은 클래스를 ‘단순’한 이름으로 정의하는 것에 focusing
    • 인터페이스: 클래스 정의 시, 메서드와 속성만 정의한 것(추상화의 본질)
  • Inheritance(상속)
    • 기본 클래스(부모)의 특징을 파생 클래스(자식)가 물려받는
      • 즉 부모 클래스의 속성이나 메서드를 자식 클래스가 그대로 물려받아 사용 가능(속성이나 메서드 추가 가능)
  • Polymorphism(다형성)
    • 다양한 형태를 가지는 것 -> 단일 작업을 다른 형태로 수행하는 것
      • ex) 같은 이름의 메서드가 조금씩 다르게 작동

객체지향 차이점

JavaScript가 다른 객체 지향 언어와 다른 점

  • 은닉화(private 키워드)의 한계
    • JavaScript는 private 키워드 없이 클로저 모듈 패턴을 활용해 은닉화 구현
    • ES2019부터 # 키워드 도입
  • 추상화(interface 키워드)의 부재

프로토타입

프로토타입과 클래스

프로토타입이란

  • 모델의 청사진(클래스)을 만들 때 쓰는 원형 객체
  • JavaScript는 프로토타입 기반 언어, DOM도 프로토타입으로 상속 구현
  • 보통 클래스의 인스턴스는 new 키워드로 생성, DOM은 new 키워드가 아닌 createElement 사용

__proto__ 속성

  • 특정 객체의 프로토타입 객체

prototype 속성

  • 상속 시키려는 멤버(상속 받는 멤버)들이 정의된 객체
  • 프로토타입 체인을 통해 상속하고자 하는 속성과 메소드를 담아두는 버킷으로 사용
    -> 객체 내부에 인스턴스가 사용할 메서드 저장
  • 상속되는 속성과 메서드는 각 객체가 아닌 객체의 생성자의 prototype이라는 속성에 정의
  • 프로토타입에는 다양한 메서드 존재

클래스, 인스턴스, 프로토타입의 관계

  • new 연산자로 생성자 함수를 호출해 새로운 인스턴스를 변수 person1에 할당
  • 인스턴스의 __proto__(프로토타입 체인에 의해 생략)가 생성자의 prototype 속성을 참조 -> person1.__proto__.greeting() 이 아닌 person1.greeting() 으로 prototype에 있는 메소드에 접근 가능
  • 인스턴스는 생성자 함수의 prototype에 있는 속성이나 메소드에 접근 가능
  • 생성자 함수의 속성인 prototype 객체 내부에 있는 constructor 속성
    -> 생성자 함수 참조, 이 속성을 통해 인스턴스의 원형을 알 수 있음

프로토타입 체인

프로토타입 체인
각각의 객체는 [[prototype]]이라는 private(은닉) 속성을 가지는데 이 속성은 자신의 프로토타입이 되는 다른 객체를 가리키고 그 객체는 그 객체의 프로토타입의 프로토타입을 가리키며 이 과정을 반복하며 null을 프로토타입으로 가지는 객체에서 끝나며 프로토타입 체인의 종점 역할을 함
어떤 객체 안에 원하는 속성이 있으면 그 객체 안의 속성 값을 리턴하고 원하는 프로퍼티가 없으면 생성자를 찾아 그 객체의 prototype 객체를 확인하여 그 객체의 프로퍼티 중 원하는 프로퍼티의 값이 있으면 그것을 반환, 없으면 또 생성자 객체의 생성자를 찾아 그 객체의 prototype 객체를 확인하여 원하는 프로퍼티 값을 찾는다.(있을 때까지 찾아 올라감 -> chain 사용)

  • JavaScript에서 ‘상속’을 구현할 때 사용
    • 상속: 부모 클래스의 속성과 메서드를 자식 클래스가 물려받는 과정
    • extends와 super 키워드를 이용해서 상속 구현 가능
  • 구현 예시
    //Human 클래스 생성
    class Human {  
        constructor(name, age) {
            this.name = name;
            this.age = age;
        }
        greeting() {
            console.log(`Hi! I'm ${this.name}.`);
        }
    } 
    // Author 클래스는 Human 클래스로부터 상속받음(extends 키워드 사용)
    // 부모 클래스의 생성자 참조(super 키워드 사용)
    class Author extends Human { 
        constructor(name, age, bookname, publisher){
            super(name, age); 
            this.bookname = bookname;
            this.publisher = publisher;
        }
    }
    let author1 = new Author('Hermann Hesse', 120, 'demian', 'Harper Perennial'); // Author 클래스의 새로운 인스턴스를 변수 author1에 할당
    author1.greeting(); // 부모 클래스의 메서드 접근 가능
    author1.name; // 부모 클래스의 속성 접근 가능
    author1.bookname; // 자식 클래스에서 추가한 속성 접근 가능
  • div요소의 프로토타입 체인 확인하기
    • div -> HTMLDivElement -> HTMLElement -> Element -> Node -> EventTarget -> Object 순
    • Object는 모든 클래스의 조상

참조링크
참조링크
참조링크
참조링크

알아두면 좋을 내용

  • super 키워드

    • 부모 객체의 함수를 호출할 때 사용
    • 생성자 함수 내에서 한 번만 사용 가능
    • this 키워드가 사용되기 전에 호출되어야 함
    • 부모 객체의 함수를 호출하는데 사용될 수 있음
      super 키워드
  • extends 키워드

    • 클래스를 다른 클래스의 자식 클래스로 만들기 위해 사용
    • 형식: class 자식 클래스 extends 부모 클래스
    • JavaScript에서는 다중 상속 불가
  • ES6 문법으로 작성한 클래스 상속 코드를 ES5 문법으로 작성해보기

    상황: Student 클래스가 Human 클래스를 상속 받는 경우

    // ES5 문법으로 작성한 클래스 상속  
    function Human (name, age) {
        this.name = name;
        this.age = age;
    }
    Human.prototype.sleep = function () {
        return `${this.name}이(가) 잠을 잡니다.`
    }

    function Student (name, age, grade) {
        Human.call(this, name, age); // Human 생성자함수를 call로 호출하여 name과 age를 가져옴
        this.grade = grade;
    }

    Student.prototype = Object.create(Human.prototype); // 부모 클래스의 함수를 가져옴
    Student.prototype.constructor = Student; // create 함수를 통해 Human으로 바뀐 생성자함수를 Student로 변경, 그렇지 않을 경우 하위 클래스의 변경이 상위 클래스의 변경에도 영향을 미침
    Student.prototype.study = function (num) {
        this.grade += num;
        return `${this.name}의 성적이 ${num}만큼 올라 ${this.grade}이 되었습니다.`;
    }
    let mincoding = new Student('민학생', 19, 70); // Student 클래스의 인스턴스 생성
    mincoding.study(10); //'민학생의 성적이 10만큼 올라 80이 되었습니다.' 출력
  • 인터페이스 이해하기(typescript)
    • 기본적으로 Javascript에는 interface 키워드가 없음
    • 인터페이스는 구현해야 하는 속성과 메서드만 설명, 메서드가 작동하는 방식은 설명하지 않음
      -> 이와 같이 인터페이스 단순화를 통해 추상화 구현
    • 인터페이스는 직접 인스턴스 생성 불가
    • TypeScript에서의 인터페이스 구현
      • interface 키워드로 interface를 정의
      • 클래스 선언문 뒤에 implements 키워드로 인터페이스 선언
      • 이 경우 해당 클래스는 지정된 인터페이스(인터페이스에서 정의한 속성과 메서드)를 반드시 구현해야 함
        typescript 인터페이스
  • #으로 은닉화 구현해보기
    • 기본적으로 JavaScript의 class 속성은 기본적으로 public 하며 클래스 외부에서 접근, 수정 가능
      ES2019부터 #을 추가해 private 클래스 필드를 선언 가능
      • scope 밖에서 캡슐화한 속성에 접근 불가능
    // Human 클래스 내부 name 속성 은닉화 구현
    class Human {
        #name
        constructor(age){
            this.#name = 'goo'; 
            this.age = age;
        }
        sleep(){ return `${this.#name}이(가) 잠을 잡니다.`};
    } 

    // 새로운 인스턴스 생성
    let person1 = new Human(16); 
    person1.age;  // 결과값 16, 접근 가능
    person1.name; // 결과값 undefined, 접근 불가능(은닉화 구현)
    person1.sleep(); // 결과값 'goo이(가) 잠을 잡니다.', 접근 가능

# 은닉화

  • getter와 setter로 구현해보기

    생성한 클래스 인스턴스의 속성 값을 변경하거나 최종 값을 예측할 수 없는 경우 사용

    • getter와 setter는 쌍으로 동작
    • getset 이용
    // Student 클래스의 grade 속성을 변경하고 싶은 상황 가정

    // Human 클래스
    class Human {
        constructor(name, age){
            this.name = name; 
            this.age = age;
        }
        sleep(){ return `${this.name}이(가) 잠을 잡니다.`}
    }

    // Human 클래스를 상속받은 Student 클래스
    class Student extends Human{
        constructor(name, age, grade){
            super(name, age);
            this._grade = grade;
        }
        study(num){
            this._grade = this._grade + num;
            return `${this.name}의 성적이 ${num}만큼 올라 ${this._grade}이 되었습니다.`
        }
        // getter
        get grade() {
            return this._grade;  
        }
        // setter
        set grade(newGrade) {
            this._grade = newGrade;
        }
    }

    // 새로운 인스턴스 생성
    let mincoding = new Student('민학생', 19, 70); 
    mincoding._grade; // 결과값은 70
    mincoding._grade = 90; // 인스턴스의 속성 값 변경
    mincoding.study(10); // '민학생의 성적이 10만큼 올라 100이 되었습니다.' 출력
    // 즉, getter와 setter를 이용해 인스턴스의 속성 값 변경 가능

getter와 setter

<오늘의 일기>
이해하기 만만치 않았던 unit2. 특히 프로토타입 속성이 나에게는 굉장히 추상적으로 다가와 이해하기 쉽지 않았고 아직까지도 남들에게 설명할 수 있을 정도로 완벽하게 이해하지는 못했지만 그래도
__proto__ 가 무엇을 가리키며 prototype은 무엇을 가리키는지 정도는 이해한 것 같다.
이번 유닛에서는 class상속에 대한 과제를 수행했는데 이번 페어분께서는 굉장히 꼼꼼하게 의사코드를 작성하시는 모습을 보고 그 점을 배워야겠다는 생각이 들었다.
쉽지 않은 여정임은 분명하지만 하나하나 완벽하게 이해하기보다는 틀을 잡고 그 위에 계속해서 살을 덧붙여 가다보면 언젠가는 어떠한 형태로 완성될 수 있을 것이라 생각하며 계속해서 공부해나가야겠다.

0개의 댓글