CodeStates/JavaScript

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

디스페어 2022. 2. 28.

Object-Oriented Programming

하나의 모델이 되는 청사진(blueprint)을 만들고 (Class),그 청사진을 바탕으로 한 객체를(object) 만드는 (instance) 프로그래밍 패턴
  • 속성과 메소드가 하나의 "객체"라는 개념에 포함
    *자바스크립트 내장 타입인 object(이하, obj literal)와는 다르게 Class라는 이름 사용
  • instance : 청사진을 바탕으로 만들어진 인스턴트 객체 (instance object)
  • 절차 지향 프로그래밍과 달리 데이터와 기능을 한곳에 묶어서 처리
    *절차적 언어 : 순차적인 명령의 조합
  • 자바스크립트 : 객체 지향 언어는 아니지만 객체 지향 패턴으로 작성 가능

 

 

OOP의 특징

1. 캡슐화(Encapsulation)

  • 데이터(속성)와 기능(메소드)을 하나의 단위로 묶는 것
  • 코드가 복잡하지 않고 재사용 가능하도록 만들어줌

 

1-1. 은닉(hiding)

const Monkey = function(name) {
  this.name = name
}

const Ukkikki = new Monkey('Ukkikki')
Ukkikki.name = 'Despair'

console.log(Ukkikki)
// Monkey {name: "Despair"}
// Ukkikki의 이름이 "Despair"로 바뀜
  • 은닉화 : 구현은 숨기고 객체 외부에서 필요한 동작(메소드)만 노출시켜 내부 데이터나 내부 구현이 외부로 노출되지 않도록 함
  • 엄격한 클래스 : 속성의 직접적인 접근을 막고, 설정하는 함수(setter)와 불러오는 함수(getter)를 철저하게 구분하기도 함

 

const Monkey = function(name) {
  const a = name
  this.getName = function() {
    console.log(a)
  }
}

const Ukkikki = new Monkey('Ukkikki')
Ukkikki.name = 'Despair'

console.log(Ukkikki)
//Monkey {name: "Despair", getName: ƒ}

Ukkikki.getName()
//Ukkikki
//외부에선 값을 얻을 수만 있고 바꿀 순 없음
  • 클로저를 활용한 방법

 

class Monkey {
  #name
  constructor(name) {
    this.#name = name
  }
}

const Ukkikki = new Monkey('Ukkikki')
Ukkikki.#name = 'Despair'
//에러 발생 : Private field '#name' must be declared in an enclosing class
  • #키워드를 사용한 방법(only class) : #name

 

1-2. 느슨한 결합(Loose Coupling)에 유리

  • 언제든 구현 수정 가능
  • 느슨한 결합 : 코드 실행 순서에 따라 절차적으로 코드를 작성하는 것이 아닌, 코드가 상징하는 실제 모습과 닮게 코드를 모아 결합

 

2. 추상화(Abstraction)

  • 내부 구현은 복잡하지만 실제로 노출되는 부분은 단순하게 만드는 것
  • 단순한 인터페이스 : 너무 많은 기능들이 노출되지 않아 예기치 못한 사용상 변화 방지

 

3. 상속(Inheritance)

  • 부모 클래스의 특징을 자식 클래스가 물려받는 것
    *기본 클래스(base class)의 특징을 파생 클래스(derive class)가 상속받는 것
  • 자식 클래스만의 특징 : 추가적으로 속성/메소드 추가 가능

 

4. 다형성(Polymorphism)

  • 다양한 형태를 가질 수 있는 특징 : 똑같은 메소드라도 다른 방식으로 구현 가능

 

 

클래스를 이용한 모듈화

1. 객체

  • 속성(property): 키-값 쌍을 의미
  • 메소드 호출 : 객체.메소드()
    *화살표 함수 사용 불가

 

2. 클래스

2-1. ES6

class Monkey {
  // 생성자(constructor) 함수
  constructor(name, color, age) { // 속성
    this.name = name
    this.color = color
    this.age = age
  }
  sayHello() { // 메소드
    return `Hello, I'am ${this.name}`
  }
}

console.log(Monkey.prototype)
// {constructor: ƒ, sayHello: ƒ}
// 	constructor: class Monkey
// 	sayHello: ƒ sayHello()
// 	__proto__: Object

const Ukkikki = new Monkey('Ukkikki', 'Black', 7)
console.log(Ukkikki)
// Monkey {name: "Ukkikki", color: "Black", age: 7}
// 	age: 7
// 	color: "Black"
// 	name: "Ukkikki"
// 	__proto__:
// 	  constructor: class Monkey
// 	  sayHello: ƒ sayHello()
// 	  __proto__: Object
for(key in Ukkikki) {
  console.log(key)
}
// name, color, age
// 메소드는 제외됨
  • 하나의 모델이 되는 청사진(blueprint)
  • 클래스명 : 대문자로 시작하는 일반명사
    *일반 함수 : 적절한 동사를 포힘, 소문자로 시작
  • 속성 : name, color, age
  • 생성자(constructor) 함수 : 인스턴스가 만들어질 때 실행되는 코드로 return 값을 만들지 않음
    *this : 인스턴스 객체를 의미
  • 메소드 : 객체에 딸린 함수를 의미, 생성자 함수와 함께 class 키워드 안쪽에 묶어서 정의

 

2-2. ES5

// 생성자(constructor) 함수
function Monkey(name, color, age) { // 속성
  this.name = name
  this.color = color
  this.age = age
  this.eat = function() {
    console.log('eating...')
  }
}

Monkey.prototype.legs = 4
Monkey.prototype.sayHello = function() { // 메소드
  return `Hello, I'am ${this.name}`
}

// 위의 코드는 아래처럼도 가능하지만 아래의 경우 constructor 명시 필요
// Monkey.prototype = {
//   constructor : Monkey,
//   legs = 4,
//   sayHello = function() { // 메소드
//   	return `Hello, i'am ${this.name}`
//   }
// }

console.log(Monkey.prototype)
// {legs: 4, sayHello: ƒ, constructor: ƒ}
// 	legs: 4
// 	sayHello: ƒ ()
// 	constructor: ƒ Monkey(name, color, age)
// 	__proto__: Object

const Ukkikki = new Monkey('Ukkikki', 'Black', 7)
console.log(Ukkikki) 
// Monkey {name: "Ukkikki", color: "Black", age: 7, eat: ƒ}
// 	age: 7
// 	color: "Black"
// 	eat: ƒ ()
// 	name: "Ukkikki"
// 	__proto__:
// 	  legs: 4
// 	  sayHello: ƒ ()
// 	  constructor: ƒ Monkey(name, color, age)
// 	  __proto__: Object
  • prototype 키워드를 사용해 프로토타입에 저장 가능

 

3. 인스턴스

let Ukkikki = new Monkey('Ukkikki', 'Black', 7)

Ukkikki.color // Black
Ukkikki.age // 7
Ukkikki.sayHello() // Hello, i'am Ukkikki
  • 청사진(blueprint)을 바탕으로 만든 객체
  • 인스턴스 만들기 : new 키워드 사용
  • 생성자 함수가 실행되며 변수에 클래스의 설계를 닮은 새로운 객체(인스턴스) 생성
  • 인스턴스 : 고유한 속성과 메소드를 갖게 됨

 

 

Prototype

const Monkey = {
  legs: 4,
  sleep() {
    console.log("slepping...")
  }
}

const Ukkikki = {
  name : "Ukkikki",
  color : "Black"
}

Ukkikki.__proto__ = Monkey

console.log(Ukkikki.legs) //4
// Ukkikki 객체엔 legs가 없지만 __proto__로 Monkey을 상속받음
// Ukkikki.legs : Ukkikki 객체에 legs가 없으므로  __proto__에서 legs를 찾음
for (key in Ukkikki) {
  console.log(key)
}
// name, color, legs, sleep
// 상속받은 key까지 모두 나옴
// .hasOwnProperty를 이용하면 for in 문에서도 상속된 property는 나오지 않게 할 수 있음

Object.keys(Ukkikki) // ["Ukkikki", "Black"]
Object.values(Ukkikki) // ["Ukkikki", "Black"]
// 상속된 property는 나오지 않음
const Ukkikki2 = {
  name : "Ukkikki2",
  color : "gray"
}

Ukkikki2.__proto__ = Ukkikki
console.log(Ukkikki2.legs) //4
// 상속은 계속됨
// legs가 Ukkikki에 없어 Ukkikki로 올라가 찾고, 
// Ukkikki에도 없으면 Monkey으로 올라가 찾음 = Prototype Chain
const Ukkikki = {
  name : "Ukkikki",
  color : "Black",
  legs : 5
}
console.log(Ukkikki.legs) //5
// Ukkikki.legs : Ukkikki 객체에 legs있으므로 Ukkikki 객체에서 legs를 찾음
  • JavaScript : 프로토타입 기반 언어로, 모든 객체들이 메소드와 속성들을 상속 받기 위한 템플릿으로써 프로토타입 객체(prototype object)를 가짐
  • 프로토타입 객체도 또 다시 상위 프로토타입 객체로부터 메소드와 속성을 상속 받을 수도 있고 그 상위 프로토타입 객체도 마찬가지(= 프로토타입 체인)
  • 상속되는 속성과 메소드들은 각 객체가 아니라 객체의 생성자의 prototype이라는 속성에 정의되어 있는 것
  • 프로토타입 : 모델의 청사진을 만들 때 쓰는 원형 객체(original form)

 

1. 클래스와 프로토타입

Monkey.prototype.constructor === Monkey // true
Monkey.prototype === Ukkikki.__proto__ // true
Monkey.prototype.sayHello === Ukkikki.sayHello // true

  • Monkey의 prototype의 생성자는 Monkey
  • Monkey의 prototype엔 sayHello가 존재
  • Ukkikki.sayHello는 Monkey.prototype.sayHello를 '언더 proto'로 참조
  • new Monkey을 통해 Ukkikki 인스턴스 생성

 

2. 프로토타입 체인

  • 프로토타입 체인(prototype chain) : 다른 객체에 정의된 메소드와 속성을 한 객체에서 사용할 수 있도록 해줌
  • 상속 : 자바스크립트에서 구현할 때 프로토타입 체인 사용
    *extends와 super 키워드를 이용
  • extends : 상속할 때 사용
  • super :
    - 자식 클래스에 constructor를 쓰고 싶을 때 사용
    - 자식 메소드에 부모의 메소드와 동일한 이름의 메소드가 있는 경우 부모의 메소드를 그대로 쓰면서 확장하고 싶을 때 사용
class Person {
  constructor(name) {
    this.name = name
  }
  sleep() { 
    console.log('sleeping...')
  }
}

const Nightmarish = new Person('Nightmarish')

class Student extends Person{
  constructor(name, age) {
    super(name)
    // super 키워드가 없으면 에러 발생
    // super를 통해 먼저 부모의 constructor를 실행해줘야 함
    // 이때 인자도 받아서 넘겨줘야 함
    this.age = age
  }
  sleep() { 
    super.sleep()
    // super 키워드가 없으면 자식 메소드로 덮어씌워져 'zzz...'만 나옴
    // super 키워드 사용 시 부모의 메소드 사용으로 'sleeping...'도 나옴
    console.log('zzz...')
  }
  learn() { 
    console.log('learning...')
  }
}

const Despair = new Student('Despair', 16) 

console.log(Despair)
// Student {name: "Despair", age: 16}
// 	age: 16
// 	name: "Despair"
// 	__proto__: Person
// 	  constructor: class Student
// 	  learn: ƒ learn()
// 	  sleep: ƒ sleep()
// 	  __proto__:
// 	    constructor: class Person
// 	    sleep: ƒ sleep()
// 	    __proto__: Object

Despair.name // 'Despair'
Despair.learn() // 'learning...'
Despair.sleep() // 'sleeping...', 'zzz...'

Despair instanceof Student // true
Despair instanceof Person // true
Despair.__proto__ // Person

 

 

References

상속과 프로토타입
클래스

반응형

'CodeStates > JavaScript' 카테고리의 다른 글

[JSON] JavaScript Object Notation  (1) 2022.03.14
[JavaScript] 재귀함수  (1) 2022.03.14
[JavaScript] 고차함수  (0) 2022.02.27
[JavaScript] 원시 자료형과 참조 자료형  (0) 2022.02.09
[JavaScript] 호이스팅  (0) 2022.01.21

댓글