[JS] 객체 지향 프로그래밍

jung_ho9 개발일지·2022년 11월 18일
0

JavaScript

목록 보기
10/19
post-thumbnail
post-custom-banner

객체 지향 개요


객체 지향 프로그래밍(OOP, Object-oriented programming)은 절차 지향 프로그래밍과 다르게 데이터와 기능을 한곳에 묶어 처리한다. 속성과 메서드가 하나의 "객체"라는 개념에 포함되며, 이는 자바스크립트 내장 타입인 Object와는 다르게 클래스라는 이름으로 부른다.

메서드 호출 실습


메서드 호출은 객체.메서드() 와 같이 객체 내에 메서드를 호출하는 방법을 의미한다.

메서드 호출 예제


단순 객체를 사용한 예제

let counter1 = {
  value: 0,
  increase: function() {
    this.value++ // 메서드 호출을 할 경우, this는 counter1을 가리킵니다
  },
  decrease: function() {
    this.value--
  },
  getValue: function() {
    return this.value
  }
}

counter1.increase() //1
counter1.increase() //2
counter1.increase() //3
counter1.decrease() //2
counter1.getValue() //2

메서드 호출 방식을 이용할 때에는 화살표 함수를 쓰지 않는 이유

화살표 함수는 자신의 this가 없어 화살표 함수를 둘러싸는 렉시컬 범위(lexical scope)의 this가 사용된다. 때문에 현재 범위에서 존재하지 않는 this를 찾을 때, 화살표 함수는 바로 바깥 범위에서 this를 찾는것으로 검색을 끝내게 된다.

const person = {
  name: "jungho",
  hello: () => console.log(`hello ${this.name}`)
};
person.hello(); // hello
  • 화살표 함수의 this 는 메소드를 호출한 객체를 가리키지 않고 상위 컨텍스트인 전역 객체, window 를 가리키게 된다.

클로저를 이용해 매번 새로운 객체 생성하기


위의 단순 객체를 사용한 예제에서는 단 하 나의 객체만 만들 수 있었다. 만약 똑같은 기능을 하는 카운터가 여러 개가 필요하다면, 같은 코드를 그대로 복사/붙여넣기 해야하므로 재사용성이 떨어진다.

똑같은 기능을 하는 카운터를 여러 개 만드는 방법 중 하나는, 아래 예제 코드와 같이 클로저 모듈 패턴을 이용할 수 있다.

function makeCounter() {
  let value = 0;
  return {
    increase: function() {
      value++;
    },
    decrease: function() {
      value--;
    },
    getValue: function() {
      return value;
    }
  }
}

let counter1 = makeCounter()
counter1.increase() // 1
counter1.getValue() // 1

let counter2 = makeCounter()
counter2.decrease() // -1
counter2.decrease() // -2
counter2.getValue() // -2

클래스와 인스턴스


객체를 생성하기 위한 하나의 모델이 되는 청사진을 클래스(class) 라고 부르고 그 청사진을 바탕으로 만들어진 객체들을 인스턴스 객체 줄여서 인스턴스(instance) 라고 부른다.

객체는 일반적인 함수를 정의하듯 만들지만, 이 함수를 이용하는 방법이 조금 다르다. => new 키워드를 써서 새로운 인스턴스를 만들어 사용한다.

인스턴스 생성 문법

function Car(color) {} //class

let mini = new Car('red') //instance
let avante = new Car('blue') // instance

인스턴스를 만들 때에는 new 키워드를 사용하며 즉시 생성자 함수가 실행되어 변수에 클래스의 설계를 가진 새로운 객체, 즉 인스턴스가 할당된다. 이때 각각의 인스턴스는 클래스의 고유한 속성과 메서드를 갖게 됩니다.

클래스 생성 문법

ES5) 클래스는 함수로 정의할 수 있다

function Car(brand , name, color ) {
//인스턴스가 만들어질 때 실행되는 코드
}

ES6) class라는 키워드를 이용해서 정의할 수도 있다.

class Car{
	constructor(brand, name, color )
    //인스턴스가 만들어질 때 실행되는 코드
}

클래스는 보통 대문자로 시작하며 일반명사로 만든다.

여기서 보이는 함수는, 객체지향 프로그래밍에서 생성자(constructor) 함수라고 부르고 인스턴스가 만들어질 때 실행되는 코드다. 참고로 생성자 함수는 return 값을 만들지 않습니다

속성과 메소드


클래스 : 속성의 정의 (this)

ES5) 클래스는 함수로 정의할 수 있다

function Car(brand , name, color ) {
	this.brand = brand;
    this.name = name;
    this.color = color ;
}

ES6) class라는 키워드를 이용해서 정의할 수도 있다.

class Car{
	constructor(brand, name, color )
    this.brand = brand;
    this.name = name;
    this.color = color;
}

객체지향프로그래밍에서 빠지지 않고 등장하는 this는, 인스턴스 객체를 의미한다. parameter로 넘어온 브랜드, 이름, 색상 등은 인스턴스 생성 시 지정하는 값이며, 위와 같이 this에 할당한다는 것은 만들어진 인스턴스에 해당 브랜드, 이름, 색상을 부여하겠다는 의미를 가진다.

클래스 : 메서드 정의

ES5) 클래스는 함수로 정의할 수 있다.

Car.prototype.refuel = function() {
}

ES5는 prototype이라는 키워드를 사용해서 메소드를 정의한다.

ES6) class라는 키워드를 이용해서 정의할 수도 있다.

class Car {
	constructor(brand, name, color } {} 
    refuel() {}
    drive() {
    	console.log(`${this.name}가 운전을 시작합니다.`)
    }
}

ES6에서는 refuel() {}, drive() {}와 같이 생성자 함수와 함께 class 키워드 안쪽에 묶어서 정의한다.

인스턴스에서의 사용

방금 작성한 속성과 메서드를 어떻게 인스턴스에서 사용하는지 아는 것이 중요하다.

let avante = new Car('hyundai' , 'avante' , 'black');
avante.color ; // black
abante.drive(); //아반떼가 운전을 시작합니다.

용어 정리


  • prototype : 모델의 청사진을 만들 때 쓰는 원형 객체
  • constructor : 인스턴스가 초기화될 때 생성하는 생성자 함수
  • this : 함수가 실행될 때, 해당 scope마다 생성되는 고유한 실행 new 키워드로 인스턴스를 생성했을 때에는 해당 인스턴스가 바로 this 의 값이 됨

객체 지향 프로그래밍


절차적 언어

  • 초기의 프로그래밍 언어는 일반적으로 절차적 언어라고 부름
  • 절차적 언어는 순차적인 명령의 조합

객체 지향 언어

  • '클래스'라고 부르는 데이터 모델의 청사진을 사용해 코드 작성
  • 현대의 언어들은 대부분 객체 지향의 특징을 갖고 있음
  • JavaScript: 객체 지향으로 작성 가능
  • 이터와 기능이 별개로 취급되지 않고, 한 번에 묶여서 처리

객체지향 프로그밍은 객체로 그룹화된다. 이 객체는 한 번 만들고 나면, 메모리 상에서 반환되기 전까지 객체 내의 모든 것이 유지된다.

객체 내에는 "데이터와 기능이 함께 있다"라는 원칙에 따라 메서드와 속성이 존재한다. 그 예로, 모든 자동차는 공통적인 기능과 고유의 속성이 있다. 속도를 낸다든지, 주유를 한다든지 등의 기능이 존재하며, 속성으로는 색상, 최고 속력 혹은 탑승인원 등과 같은 고유의 데이터가 존재하는 것과 같다. 그렇기 때문에, 새로운 객체를 만들 때, "이번에 만들 자동차는, 빨간색의 최고 속력은 200km/h를 내도록 만들어보자!"와 같이, 속성에 고유한 값을 부여할 수 있다.

클래스의 생성자

생성자를 통해 세부 사항(속성)을 넣어줍니다. 함수에 인자를 넣듯, 속성을 넣을 수 있습니다.

class Car{
---- 생성자 ---- 
	constructor(brand, name, color )
    this.brand = brand;
    this.name = name;
    this.color = color;
 --------------
}

객체 지향 프로그래밍의 개념


1. 캡슐화(Encapsulation)

  • 데이터와 기능을 하나의 단위로 묶는 것
  • 구현은 숨기고, 동작은 노출시킴
  • 느슨한 결합에 유리, 언제든 구현을 수정할 수 있음
  • 코드나 데이터 은닉에 포커스

2. 상속(Inheritance)

  • 부모 클래스의 특징을 자식 클래스가 물려받는 것

3. 추상화(Abstraction)

  • 내부 구현은 아주 복잡한데, 실제 노출되는 부분은 단순하게 만드는 개념
  • 추상화는 클래스를 사용하는 사람이 필요하지 않은 메서드 등을 노출시키지 않고, 단순한 이름으로 정의하는 것에 포커스
  • 속성과 메서드의 이름만 노출시켜서 사용을 단순화한다는 의미.

4. 다형성(Polymorphism)

  • 다양한 형태의 의미를 가짐
  • 객체 역시 똑같은 메서드라 하더라도, 다른 방식으로 구현됨

객체 지향 프로그래밍의 장점


  1. 캡슐화는 코드가 복잡하지 않게 만들고, 재사용성을 높임
  2. 추상화는 마찬가지로 코드가 복잡하지 않게 만들고, 단순화된 사용으로 변화에 대한 영향을 최소화함
  3. 상속 역시 불필요한 코드를 줄여 재사용성을 높임
  4. 다형성으로 인해 동일한 메서드에 대해 if/else if와 같은 조건문 대신 객체의 특성에 맞게 달리 작성하는 것이 가능

은닉화 (private 키워드)의 한계


Java나 TypeScript라는 프로그래밍 언어는 클래스 내부에서만 쓰이는 속성 및 메서드를 구분시키기 위해 private이라는 키워드를 제공한다. .하지만 Javascript의 경우 은닉화를 도와주는 기능을 지원하는 브라우저가 적다.

아래는 TypeScript의 예제로, name이라는 속성이 존재한다. 그러나 private 키워드가 붙어 있어서, 클래스 내부에서만 사용 가능하다.

// TypeScript 문법입니다.

class Animal {
  private name: string;

  constructor(theName: string) {
    this.name = theName;
  }
}

new Animal("Cat").name; // 사용 불가
// Property 'name' is private and only accessible within class 'Animal'. 

JavaScript에서는 은닉화를 돕기 위해서 일반적으로 클로저 모듈 패턴을 사용. 클래스/인스턴스 형태로 만들 때에는 ES2019부터 #이라는 키워드가 도입되었음

추상화(interface 키워드) 기능의 부재


객체 지향 프로그래밍의 주요 키워드 중 하나인 추상화는, 속성과 메서드의 이름만 노출시켜서 사용을 단순화한다는 의미를 갖고 있다. 즉, 인터페이스(interface)의 단순화를 의미하며 Java나 TypeScript 언어는 언어의 주요 기능으로 interface를 구현해 놓았지만, JavaScript에는 존재하지 않는 기능이다.

프로토타입 개요


JavaScript의 프로토타입 기반 언어, 기서 프로토타입(Prototype)은 원형 객체를 의미한다.
모든 객체들이 메소드와 속성들을 상속 받기 위한 템플릿으로써 프로토타입 객체(prototype object) 를 가진다는 의미이다. 정확히 말하자면 상속되는 속성과 메소드들은 각 객체가 아니라 객체의 생성자의 prototype이라는 속성에 정의되어 있습니다.

class Human {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  sleep() {
    console.log(`${this.name}은 잠에 들었습니다`);
  }
}

let kimcoding = new Human('김코딩', 30);

Human.prototype.constructor === Human; // true
Human.prototype === kimcoding.__proto__; // true
Human.prototype.sleep === kimcoding.sleep; // true
  • Human 클래스의 생성자 함수는 Human이다.
  • Human 클래스의 프로토타입은 Human 클래스의 인스턴스인 kimcoding의 proto이다.
  • Human 클래스의 sleep 메서드는 프로토타입에 있으며, Human 클래스의 인스턴스인 kimcoding에서 - kimcoding.sleep으로 사용할 수 있다.

Human이라는 클래스와 인스턴스, 그리고 프로토타입의 관계


Array(배열) 클래스와 인스턴스, 그리고 프로토타입의 관계


업로드중..

프로토타입 체인을 사용한 상속 예제

  • Person을 class를 상속받아 Teacher라는 class를 만든다면, 아래 예제와 같이 extendssuper 키워드를 사용한다.

  • 모든 클래스의 최상위 클래스는 Object다.

class Teacher extends Person {
  constructor(first, last, age, gender, interests, subject, grade) {
    this.name = {
      first,
      last
    };

  this.age = age;
  this.gender = gender;
  this.interests = interests;
  // subject and grade are specific to Teacher
  this.subject = subject;
  this.grade = grade;
  }
}

super키워드는 부모 오브젝트의 함수를 호출할 때 사용한다 생성자에서 super키워드는 하나만 사용되거나 this키워드가 사용되기 전에 호출되어야 한다. 이 예제와 같이 Person class를 따로 수정하지 않고 Teacher를 생성한 것 처럼 다른 하위 클래스를 생성할 수 있다.

class Teacher extends Person {
  constructor(first, last, age, gender, interests, subject, grade) {
    super(first, last, age, gender, interests);

    // subject and grade are specific to Teacher
    this.subject = subject;
    this.grade = grade;
  }
}

.__proto__

.__proto__를 이용하면 부모 클래스의 프로토타입, 혹은 '부모의 부모 클래스'의 프로토타입을 알 수 있다.

let div = document.createElement('div');

div.__proto__  // HTMLDivElement
div.__proto__.__proto__  // Element
div.__proto__.__proto__.__proto__ //Node
div.__proto__.__proto__.__proto__.__proto__ //EventTarget
div.__proto__.__proto__.__proto__.__proto__.__proto__ // Object
div.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__ // null
div.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__
profile
꾸준하게 기록하기
post-custom-banner

0개의 댓글