자바스크립트 객체지향

객체지향

객체지향은 크고 견고한 프로그래밍을 만들기 위한 노력의 산물 객체지향이라는 큰 흐름은 현대적 프로그래밍 언어들을 지배하고 있는 가장 중요한 맥락이라고 말할 수 있음

1. 객체지향 프로그래밍

1-1. 객체 지향 프로그래밍

객체지향 프로그래밍(Object-Oriented Programming)은 좀 더 나은 프로그램으로 만들기위한 프로그래밍 패러다임으로 로직을 상태(state)와 행위(behave)로 이루어진 객체로 만드는 것 이 객체들을 마치 레고 블럭처럼 조립해서 하나의 프로그램을 만드는 것이 객체지향 프로그래밍이라고 할 수 있음 객체지향 프로그래밍은 객체를 만드는 것.

객체는 변수와 메소드를 그룹핑한 것.

1-2. 문법과 설계

객체 지향 프로그래밍 교육은 크게 두가지로 구분 됨

1-2-1. 문법

하나는 객체지향을 편하게 할 수 있도록 언어가 제공하는 기능을 익히는 것.

1-2-2. 설계

두번째는 좋은 객체를 만드는 것. 추상화 : 복잡함 속에서 필요한 관점만을 추출하는 행위 프로그램을 만든다는 것은 소프트웨어의 추상화

1-3. 부품화

객체 지향은 객체 지향의 정점 메소드는 부품화의 예입니다. 메소드를 사용하는 기본 취지는 연관되어 있는 로직들을 결합해서 메소드라는 완제품을 만드는 것. 이 메소드들을 부품으로 해서 하나의 완제품인 독립된 프로그램을 만드는 것. 프로그램이 복잡해짐에 따라 메소드도 한계를 보임.

이 한계의 도약 중 하나가 객체 지향 프로그래밍. 객체 지향 프로그램의 핵심은 연관된 메소드와 그 메소드가 사용하는 변수들을 분류하고 그룹핑하는 것 이렇게 그룹핑 한 대상이 객체(Object) 파일과 디렉토리로 비유했을때 메소드나 변수가 파일이라면 이 파일을 그룹핑하는 디렉토리가 객체라고 할 수 있음.

1-4. 은닉화, 캡슐화

제대로 된 부품이라면 그것이 어떻게 만들어졌는지 모르는 사람도 그 부품을 사용하는 방법만 알면 쓸 수 있어야 함. 내부의 동작방법을 단단한 케이스 안으로 숨기고 사용자에게는 그 부품의 사용방법만을 노출하고 있는 것. 이러한 컨셉을 정보의 은닉화(Information Hiding) 또는 캡슐화(Encapsulation)로 불림.

1-5. 인터페이스

잘 만들어진 부품이라면 부품과 부품을 서로 교환 할 수 있어야 함. 인터페이스는 부품들간의 연결점 하드웨어에서 할 수 없는 것을 소프트웨어에서 할 수 있는데 복제와 상속.

2. 생성자와 new

2-1. 객체

객체란 서로 연관된 변수와 함수를 그룹핑한 그릇이라고 할 수 있음. 객체 내의 변수를 프로퍼티(property) 함수를 메소드(method)라고 부름

let person = {}
person.name = 'egoing'
person.introduce = function() {
  return `My name is ${this.name}`
}

console.log(person.introduce())
// result : My name is egoing
const person = {
  name: 'egoing',
  introduce: function() {
    return `My name is ${this.name}`
  },
}
console.log(person.introduce())
// result : My name is egoing

생성자 : 객체의 구조를 재활용할 수 있는 방법

2-2. 생성자

생성자(constructor)는 객체를 만드는 역할을 하는 함수 자바스크립트에서 함수는 재사용 가능한 묶음이 아니라 객체를 만드는 창조자라고 할 수 있음

function Person() {}
let p = new Person()
p.name = 'egoing'
p.introduce = function() {
  return `My name is ${this.name}`
}
console.log(p.introduce())
// result : My name is egoing
function Person(name) {
  this.name = name
  this.introduce = function() {
    return 'My name is ' + this.name
  }
}
const p1 = new Person('egoing')
console.log(p1.introduce())

const p2 = new Person('leezche')
console.log(p2.introduce())
/*
result:
My name is egoing
My name is leezche
*/

초기화 : 생성자 내에서 이 객체의 프로퍼티를 정의하고 있음 이를 통해서 코드의 재사용성이 높아짐.

생성자 함수는 일반함수와 구분하기 위해서 첫글자를 대문자로 표시함.

2-3. 자바스크립트 생성자의 특징

일반적인 객체지향 언어에서 생성자는 클래스의 소속. 하지만 자바스크립트에서 객체를 만드는 주체는 함수임.

3. 전역객체

전역객체(Global object)는 특수한 객체. 모든 객체는 이 전역객체의 프로퍼티.

function func() {
  console.log('Hello?')
}
func()
window.func()
/*
result:
Hello?
Hello?
*/

모든 전역변수와 함수는 사실 window 객체의 프로퍼티. 객체를 명시하지 않으면 암시적으로 window의 프로퍼티로 간주.

let o = {
  func: function() {
    console.log('Hello?')
  },
}
o.func()
window.o.func()

자바스크립트에서 모든 객체는 기본적으로 전역객체의 프로퍼티임을 알 수 있음.

3-1. 전역객체 API

ECMAScript에서는 전역객체의 API를 정의해줌 그 외의 API는 호스트 환경에서 필요에 따라서 추가로 정의하고 있음.

전역객체의 이름은 호스트환경에 따라 달라짐 node.js에서는 global, 웹브라우저에서는 window

4. this

this는 함수 내에서 함수 호출 맥락(context)를 의미함. 맥락이라는 것은 상황에 따라서 달라진다는 의미인데 즉 함수를 어떻게 호출하느냐에 따라서 this가 가르키는 대상이 달라진다는 뜻임. 함수와 객체의 관계가 느슨한 자바스크립트에서 this는 이 둘을 연결시켜주는 실질적인 연결점의 역할을 함.

4-1. 함수호출

함수를 호출했을 때 this는 전역객체인 window와 같음

function func() {
  if (window === this) {
    console.log('window === this')
  }
}
func()
// result: window === this

4-2. 메소드의 호출

객체의 소속인 메소드의 this는 그 객체를 가르킴

const o = {
  func: function() {
    if (o === this) {
      console.log('o === this')
    }
  },
}
o.func()
// result : o === this

4-3. 생성자의 호출

let funcThis = null

function Func() {
  funcThis = this
}
let o1 = Func()
if (funcThis === window) {
  console.log('window')
}

let o2 = new Func()
if (funcThis === o2) {
  console.log('o2')
}
/*
result:
window
o2
*/

생성자는 빈 객체를 만듬. 그리고 이 객체내에서 this는 만들어진 객체를 가르킴. 생성자가 실행되기 전까지는 객체는 변수에도 할당될 수 없기 때문에 this가 아니면 객체에 대한 어떠한 작업을 할 수 없음.

function Func() {
  console.log(o)
}
let o = new Func()
// result : Uncaught ReferenceError: Cannot access 'o' before initialization

4-4. apply, call

함수의 메소드인 apply, call을 이용하면 this의 값을 제어할 수 있음.

let o = {}
let p = {}
function func() {
  switch (this) {
    case o:
      console.log('o')
      break
    case p:
      console.log('p')
      break
    case window:
      console.log('window')
      break
  }
}
func()
func.apply(o)
func.apply(p)
/*
result:
window
o
p
*/

5. 상속

5-1. 상속(inheritance)이란?

객체는 연관된 로직들로 이루어진 작은 프로그램 상속은 객체의 로직을 그대로 물려 받는 또 다른 객체를 만들 수 있는 기능을 의미함. 기존의 로직을 수정하고 변경해서 파생된 객체를 만들 수 있게 해줌

function Person(name) {
  this.name = name
}
Person.prototype.name = null
Person.prototype.introduce = function() {
  return `My name is ${this.name}`
}
let p1 = new Person('egoing')
console.log(p1.introduce())
// result : My name is egoing
function Person(name) {
  this.name = name
}

Person.prototype.name = null
Person.prototype.introduce = function() {
  return `My name is ${this.name}`
}

function Programmer(name) {
  this.name = name
}

Programmer.prototype = new Person()

let p1 = new Programmer('egoing')
console.log(p1.introduce())
// result : My name is egoing

Programmer이라는 생성자를 만들고 이 생성의 prototype 과 Person의 객체를 연결하여 Programmer 객체도 메소드 introduce를 사용할 수 있게 됨.

Programmer가 Person의 기능을 상속하고 있는 것임. 부모의 기능을 계승 발전할 수 있는 것이 상속의 가치.

function Person(name) {
  this.name = name
}
Person.prototype.name = null
Person.prototype.introduce = function() {
  return `My name is ${this.name}`
}

function Programmer(name) {
  this.name = name
}
Programmer.prototype = new Person()
Programmer.prototype.coding = function() {
  return 'hello world'
}

let p1 = new Programmer('egoing')
console.log(p1.introduce())
console.log(p1.coding())
/*
result:
My name is egoing
hello world
*/

Programmer는 Person의 기능을 가지고 있으면서 Person이 가지고 있지 않은 기능인 메소드 coding을 가지고 있음.

6. prototype

6-1. prototype

prototype는 객체의 원형 함수는 객체 생성자로 사용될 함수도 객체 객체는 프로퍼티를 가질 수 있는데 prototype이라는 프로터피는 그 용도가 약속되어 있는 특수한 프로퍼티 prototype에 저장된 속성들은 생성자를 통해서 객체가 만들어질 때 그 객체에 생성

function Ultra() {}
Ultra.prototype.ultraProp = true

function Super() {}
Super.prototype = new Ultra()

function Sub() {}
Sub.prototype = new Super()

var o = new Sub()
console.log(o.ultraProp)
// result : true

생성자 Sub를 통해서 만들어진 객체 o가 Ultra의 프로퍼티 ultraProp에 접근 가능한 것은 prototype 체인으로 Sub와 Ultra가 연결되어 있기 때문임

  1. 객체 o에서 ultraProp를 찾는다.
  2. 없다면 Sub.prototype.ultraProp를 찾는다.
  3. 없다면 Super.prototype.ultraProp를 찾는다.
  4. 없다면 Ultra.prototype.ultraProp를 찾는다.

prototype는 객체와 객체를 연결하는 체인의 역할을 하는 것 이러한 관계를 prototype chain이라고 함

7. 표준 내장 객체의 확장

표준 내장 객체(Standard Built-in Object)는 자바스크립트가 기본적으로 가지고 있는 객체들을 의미함 내장 객체가 중요한 이유는 프로그래밍을 하는데 기본적으로 필요한 도구들이기 때문임

자바스크립트의 내장 객체

  • Object
  • Function
  • Array
  • String
  • Boolean
  • Number
  • Math
  • Date
  • RegExp

7-1. 배열을 확장

const arr = new Array('seoul', 'new york', 'ladarkh', 'pusan', 'Tsukuba')
function getRandomValueFromArray(haystack) {
  const index = Math.floor(haystack.length * Math.random())
  return haystack[index]
}
console.log(getRandomValueFromArray(arr))
Array.prototype.rand = function() {
  const index = Math.floor(this.length * Math.random())
  return this[index]
}
const arr = new Array('seoul', 'new york', 'ladarkh', 'pusan', 'Tsukuba')
console.log(arr.rand())

8. Object

Object 객체는 객체의 가장 기본적인 형태를 가지고 있는 객체 자바스크립트에서는 값을 저장하는 기본적인 단위로 Object를 사용

const grades = { egoing: 10, k8805: 6, sorialgi: 80 }

동시에 자바스크립트의 모든 객체는 Object 객체를 상속 받는데, 모든 객체는 Object 객체의 프로퍼티를 가지고 있음

또한 Object 객체를 확장하면 모든 객체가 접근할 수 있는 API를 만들 수 있음

Object.prototype.contain = function(neddle) {
  for (let name in this) {
    if (this[name] === neddle) {
      return true
    }
  }
  return false
}
const o = { name: 'egoing', city: 'seoul' }
console.log(o.contain('egoing'))
const a = ['egoing', 'leezche', 'grapittie']
console.log(a.contain('leezche'))
/*
result:
true
true
*/

Object 객체는 확장하지 않는 것이 바람직힘. 왜냐하면 모든 객체에 영향을 주기 때문.

Object.prototype.contain = function(neddle) {
  for (let name in this) {
    if (this[name] === neddle) {
      return true
    }
  }
  return false
}
const o = { name: 'egoing', city: 'seoul' }
const a = ['egoing', 'leezche', 'grapittie']
for (let name in o) {
  console.log(name)
}
/*
result:
name
city
contain
*/
for (let name in o) {
  if (o.hasOwnProperty(name)) console.log(name)
}
/*
result:
name
city
*/

Object 객체를 확장해서 나오는 문제는 hasOwnProperty로 해결 hasOwnProperty는 인자로 전달된 속성의 이름이 객체의 속성인지 여부를 판단 만약 prototype으로 상속받은 객체라면 false가 됨

9. 데이터 타입

9-1. 원시 데이터 타입

데이터 타입이란 데이터 형태를 의미함 데이터 타입은 크게 객체와 객체가 아닌 것으로 나눠짐

객체가 아닌 것

  • 숫자
  • 문자열
  • 불리언(true/false)
  • null
  • undefined

객체가 아닌 데이터 타입을 원시 데이터 타입(primitive type)이라고 함 그외의 모든 데이터 타입들은 객체

9-2. 레퍼 객체

const str = 'coding'
console.log(str.length)
console.log(str.charAt(0))
/*
result:
6
c
*/

문자열은 프로퍼티와 메소드가 있어서 객체 같이 보이지만 내부적으로 문자열이 원시 데이터 타입이고 문자열과 관련된 어떤 작업을 하려고 할 때 자바스크립트는 임시로 문자열 객체를 만들고 사용이 끝나면 제거하기 때문 이러한 처리는 내부적으로 일어남.

const str = 'coding'
str.prop = 'everybody'
console.log(str.prop)
// result : undefined

str.prop를 하는 순간 자바스크립트 내부적으로 String이 만들어짐 prop 프로퍼티는 이 객체에 저장되고 이 객체는 곧 제거됨 이렇기 때문에 prop라는 속성이 저장된 객체는 존재하지 않게 됨

하지만 문자열과 관련해서 필요한 기능성을 객체지향적으로 제공해야할 필요 또한 있기 때문에 원시형 데이터 형을 객체처럼 다룰 수 있도록 하기 위한 객체를 자바스크립트는 제공하고 있는데 그것이 래퍼 객체(wrapper object)

레퍼객체로는 String, Number, Boolean가 있음. null, undefined는 레퍼 객체가 존재하지 않음

10. 참조

10-1. 복제

let a = 1
let b = a
b = 2
console.log(a)
// result: 1

값을 변경하는 것은 변수 b이기 때문에 변수 a에 담겨있는 값은 그대로 변수 b의 값에 변수 a의 값이 복제

10-2. 참조

const a = { id: 1 }
let b = a
b.id = 2
console.log(a.id)
// result : 2

변수 b에 담긴 객체의 id 값을 2로 변경했을 뿐인데 a.id의 값도 2가 됨 이것은 변수 b와 변수 a에 담긴 객체가 서로 같다는 것을 의미함

객체를 다른 말로는 참조 데이터 형(참조 자료형)이라고 부름 모든 객체는 참조 데이터형

변수에 담겨있는 데이터가 원시형이면 그 안에는 실제 데이터가 들어있고, 객체면 변수 안에는 데이터에 대한 참조 방법이 들어있다고 할 수 있음

10-3. 함수

const a = 1
function func(b) {
  b = 2
}
func(a)
console.log(a)
// result : 1

원시 데이터 타입을 인자로 넘겼을 때의 동작 모습

const a = { id: 1 }
function func(b) {
  b = { id: 2 }
}
func(a)
console.log(a.id)
// result : 1

참조 데이터 타입을 인자로 넘겼을 때의 동작 모습

함수 func의 파라미터 b로 전달된 값은 객체 a(b=a) b를 새로운 객체로 대체하는 것은 (b={ id:2 }) b가 가르키는 객체를 변경하는 것이기 때문에 객체 a에 영향을 주지 않음

const a = { id: 1 }
function func(b) {
  b.id = 2
}
func(a)
console.log(a.id)
// result : 2

파라미터 b는 객체 a의 레퍼런스 이 값의 속성을 바꾸면 그 속성이 소속된 객체를 대상으로 수정작업을 한 것이 되기 때문에 b의 변경은 a에도 영향을 미치게 됨

패턴

패턴은 자주 사용하는 로직들의 구현방법과 그것에 대한 이름을 의미함

재귀함수

재귀함수는 함수 자신을 호출하는 프로그래밍 기법


Written by@seongjoojin
Lives and works in Seoul building useful things.

GitHubTwitter