티스토리 뷰
자바스크립트 객체지향 프로그래밍 Javascript Object Oriented Programming
이브라히모비치 2018. 11. 12. 17:20객체 생성 기본과 접근
자동차, 가게, 새 같은 모든 것들이 객체이다. 어떤 속성이 있는가? 자동차는 바퀴를 가지고 있고, 가게에서는 물건을 팔며, 새는 날개를 가지고 있다.이런 속성들이 객체를 만든다. 유사한 객체들은 동일한 속성을 공유하지만 그 값은 다양하다는 점에 주목하라. 예를 들어 모든 자동차에는 바퀴가 있지만, 바퀴 숫자는 차종마다 다른것 처럼 말이다.
자바스크립트의 객체는 실제 객체를 모델링하는데 사용되며 실제 객체와 마찬가지로 속성과 기능을 부여한다. 이런 컨셉으로 작성된 오리(duck) 객체를 살펴보자.
duck 객체는 2개의 속성과 값을 가지고 있다. 이름은 Aflac, 다리의 수는 2 이다.
속성의 값을 얻기 위해서는 점(.) 을 사용하면 된다.
메소드method
객체는 메소드(method)라 불리는 특별한 속성을 가지고 있다.
메소드는 함수(function)로 된 속성을 말하고, 객체에 기능을 추가하는 역할을 한다. duck 예제를 살펴보자.
오리의 이름을 알려주는 함수인 sayName 메소드를 추가한다.
메소드가 name 을 가져올 때 duck.name 으로 접근한 것을 주목하라. 이제 다른 방식으로 접근하는 법을 알아볼 것이다.
this 키워드를 활용한 재사용 가능한 코드 만들기
위 메소드에서 duck.name을 이용해 name 속성에 접근했다. 이 방식은 잘 동작하지만, 여기에는 함정이 있다.
만약 변수의 이름이 바뀐다면 이 방식으로 속성에 접근하는 모든 코드를 수정해줘야한다. 작은 객체에선 큰 문제가 아니겠지만, 속성을 참조하는 객체가 커지면 에러가 발생할 확률이 높아진다.
이런 이슈를 피하기 위한 방법은 다음과 같다.
여기서 this 가 가리키는 것은 duck 객체이다. 만약 객체의 이름이 mallard(청둥오리)로 바뀐다고 해도, duck을 참조하는 모든 코드를 찾을 필요가 없어진다.
코드의 가독성은 물론 재사용성도 높이는 방법이다.
생성자 함수 Constructor Function
생성자는 새로운 객체를 만드는 함수이다. 마치 청사진처럼 새 객체에 포함될 속성과 기능을 명시한다.
예를 살펴 보자.
이 생성자는 Bird 객체를 정의하고 name, color, numLegs 속성에 그 값을 각각 Albert, blue, 2 로 지정했다.
생성자는 몇가지 규칙을 따른다 :
- 생성자는 대문자로 시작한다. 이는 생성자가 아닌 다른 함수들과의 구분을 위해서다.
- 생성자는 만들어질 객체의 속성을 지정하기 위해 this 키워드를 사용한다. 생성자 함수 내부에서 this 는 만들어질 객체를 가르킨다.
- 생성자는 다른 함수처럼 값을 반환하는 대신 속성과 기능을 정의한다.
생성자를 이용한 객체 생성 Use a Constructor to Create Objects
Bird 생성자를 살펴보자.
생성자를 호출할때 new 연산자를 사용한다는 점에 주목하라. 이는 자바스크립트에게 blueBird 라고 불리는 새로운 Bird의 인스턴스를 생성하라고 이야기한다.
new 연산자가 없으면 생성자 내부에서 새로 만든 객체를 가리키지 않아 예기치 않은 결과가 발생한다.
이제 blueBird 는 Bird 생성자 내부에서 정의한 모든 속성을 가지고 있다.
다른 객체와 마찬가지로 속성에 접근하고 수정할 수 있다.
인수를 받을 수 있도록 생성자 확장하기
Bird 생성자는 잘 동작한다. 하지만 이 생성자로 생성된 모든 새 객체들은 자동으로 Albert 라는 이름과 푸른색상, 2개의 다리를 부여받는다.
만약 다른 이름과 색상의 새를 원한다면? 아래와 같은 방법으로 각각 수동으로 바꿀 수 있으나 손이 많이 간다.
당신이 새장 속에 있는 몇백, 몇천 마리의 새들을 관리하는 프로그램을 만든다고 가정해보자.
모든 새들을 생성하는데 많은 시간이 소요될 것이고, 이후 각기 다른 속성들로 바꿔줘야 할 것이다.
보다 쉽게 각기 다른 Bird 객체를 생성하기 위해, 인수parameter를 받을 수 있게 디자인 할 수 있다.
이제 인수로 각 특징들을 Bird 생성자에 전달해보자.
Bird의 새로운 인스턴스로 이름은 Bruce 이고 색상은 붉은색을 전달한다. numLegs 속성은 여전히 2로 고정되어 있다.
cardinal 가 가진 속성은 아래와 같다 :
생성자가 보다 확장성있게 설계되었다. 이제 각 Bird의 속성들을 생성되는 시점에 전달할 수 있다.
이는 자바스크립트 생성자를 굉장히 유용하게 만드는 방식 중 하나이다. 이것은 특성 및 기능을 기반으로 개체를 그룹화 하고 자동 생산을 위한 청사진을 정의한다.
instanceof 를 이용한 객체 생성자 검증
생성자 함수가 새로운 객체를 만들어 낼때, 그 객체는 생성자의 인스턴스라 불려진다. 자바스크립트에는 이를 편리하게 확인할 수 있는 instanceof 연산자가 있다.
instanceof 를 이용해 객체와 생성자를 비교할 수 있으며, 이는 해당 객체가 그 생성자로부터 생성된것인지를 판단해 true 혹은 false를 반환한다.
예제를 살펴보자.
만약 객체가 생성자로부터 만들어진것이 아니라면, instanceof 는 false를 반환한다.
own 프로퍼티의 이해
아래 예제를 따르면, Bird 생성자는 2개의 프로퍼티를 정의한다 : name 과 numLegs
name 과 numLegs 는 own 프로퍼티라고 불린다. 왜냐하면 이것들은 인스턴스 객체에 직접 정의되었기 때문이다.
이것은 duck 과 canary 가 각각 프로퍼티 복사본을 가지고 있다는 것을 의미한다.
사실 모든 Bird의 인스턴스들은 이 프로퍼티의 복사본을 소유하게 된다.
다음 예제는 duck 이 소유한 프로퍼티를 hasOwnProperty를 이용해 판단하고, ownProps 배열에 저장한다.
중복 코드를 줄이기 위한 prototype 사용
numLegs 속성은 모든 Bird 인스턴스 객체들에서 동일하기 때문에, 필수적으로 각 객체가 복사본을 가지게 된다.
단지 2개의 인스턴스만 있다면 문제 되지 않겠지만, 수백만개의 인스턴스가 있다고 가정해보자. 수많은 복제된 변수가 생기게 된다.
이를 해결하는 방법은 Bird 의 prototype 을 이용하는 것이다. prototype은 모든 Bird 인스턴스가 공유하는 객체이다.
어떻게 numLegs를 Bird의 prototype에 추가하는지 살펴보자.
모든 인스턴스들이 자동으로 prototype의 프로퍼티를 가지기 때문에, 객체를 만드는 레시피라고 생각하면 될 것이다.
duck 과 canary 가 가진 prototype은 Bird 생성자의 Bird.prototype 의 요소인 것을 주목하라.
자바스크립트의 대부분 객체는 그것을 생성한 생성자 함수의 요소로 이루어진 prototype 프로퍼티를 가지고 있다.
모든 프로퍼티 순회
지금까지 2 종류의 프로퍼티를 살펴보았다 : 소유한own 프로퍼티와 prototype 프로퍼티.
own 프로퍼티는 인스턴스 객체에서 직접 정의되었고, prototype 프로퍼티는 prototype을 통해 정의 되었다.
이제 duck 의 own 프로퍼티를 ownProps 배열로, prototype 프로퍼티를 prototypeProps 배열로 추가해보자 :
constructor 프로퍼티의 이해
생성자 Bird와 Dog를 이용해 생성된 인스턴스 객체에는 constructor 프로퍼티가 존재한다.
constructor 프로퍼티는 인스턴스 객체를 생성한 생성자 함수의 참조이다.
이것의 장점은 어떤 종류의 객체가 있는지 판단할 수 있다는 점이다. 예제를 살펴보자.
참고 :
constructor 프로퍼티는 덮어쓸 수 있기 때문에, 일반적으로 객체의 type을 확인하기 위해서는 instanceof 메소드를 사용하는 편이 낫다.
새로운 객체로 prototype 수정하기
지금까지 prototype에 개별적으로 프로퍼티를 추가해주었다.
몇가지 더 추가해보면 별로 좋은 방법이 아니라는것을 알게 된다.
더 나은 방법은 프로퍼티를 이미 가지고 있는 새로운 객체를 하나 만들어서 prototype에 넣어 주는 것이다. 이 방법은 모든 프로퍼티를 한번에 추가할 수 있다.
하지만 이 방법을 사용하면 한가지 엄청난 사이드 이펙트가 발생한다. 바로 constructor 프로퍼티가 삭제된다는 점!
아래 코드를 실행하면 이렇게 된다.
해결하기 위해, 수동으로 새로운 객체를 prototype에 적용할때마다 constructor 프로퍼티를 정의해 줘야한다.
객체의 prototype은 어디에서 온 것일까
사람들의 유전자가 부모로부터 유전되는 것 처럼, 객체도 그것을 생성한 생성자 함수로 부터 직접 prototype을 상속 받는다.
예를 들어, 여기 Bird 생성자가 duck 객체를 생성하면
duck은 Bird 생성자 함수로 부터 prototype을 상속 받는다. isPrototypeOf 메소드를 이용해 확인해 볼 수 있다.
프로토타입 체인(Prototype Chain) 의 이해
자바스크립트의 모든 객체(일부 예외)는 prototype 을 가지고 있다. 또한 객체의 prototype도 객체이다.
prototype도 객체이기 때문에, 스스로 프로퍼티를 가질 수 있다! 이런 경우, Bird.prototype의 prototype은 Object.prototype 이다.
어떻게 활용될 수 있을까? hasOwnProperty 메소드를 다시 호출해보면 될 것이다.
hasOwnProperty 메소드는 Bird.prototype에서 액세스 할 수있는 Object.prototype에 정의되어 있으며 duck 의해 액세스 될 수 있다.
이것이 프로토타입 체인(prototype chain) 의 예이다.
이 prototype chain에서, Bird는 duck의 상위타입supertype 이고, duck은 하위타입subtype이다. Object는 Bird와 duck 모두의 상위타입이다.
Object는 모든 자바스크립트 객체의 상위타입이 된다. 그러므로 어떤 객체라도 hasOwnProperty 메소드를 사용할 수 있다.
상속을 이용해 반복을 피하라
프로그래밍에서 중복하지 말라(Don't Repeat Yourself - DRY)는 원칙이 있다.
반복되는 코드가 문제되는 이유는 모든 변경 작업이 여러 위치의 코드를 수정하기 때문이다.
이는 일반적으로 개발자에게는 더 많은 작업을, 에러에 대해서는 더 많은 여지줄 수 있다는 의미다.
Bird와 Dog가 공유하는 메소드를 예제로 알아보자.
describe 메소드가 2군데에서 중복되고 있다. Animal 이라 불리는 상위타입supertype(혹은 부모)를 이용해 DRY 원칙을 따르도록 코드를 수정할 수 있다.
Animal 이 describe 메소드를 포함하고 있기 때문에, Bird와 Dog에서 지워버릴 수 있다 :
상위타입으로 부터의 상속
위 예제에서, 모든 동물들에게 공유된 행동을 정의하는 Animal 이라는 상위타입을 만들어 보았다.
이제 Animal 의 메소드를 Bird와 Dog 내부에서 재정의 하지 않고 어떻게 재사용하는지 알아보자. '상속(inheritance)'이라 불리는 기술이다.
첫번째 단계 : 상위타입(혹은 부모)의 인스턴스 생성
우리는 이미 new 연산자를 이용해서 Animal의 인스턴스를 생성하는 방법을 알고 있다 :
이렇게 상속을 구현하면 몇가지 단점이 존재한다. 그것들은 여기서 다루기엔 너무 복잡하다. 대신 그 단점들을 극복하기 위한 대안을 소개한다.
Object.create(obj)는 새로운 객체를 생성하고, obj는 새 객체의 프로토타입이 된다. 이 프로토타입은 객체를 만드는 레시피가 된다.
Animal의 프로토타입으로 animal의 프로토타입을 셋팅하게되면, animal 인스턴스에게 모든 Animal 인스턴스와 동일한 레시피를 효과적으로 전달할 수 있다.
부모의 인스턴스에 자식 프로토타입 적용하기
상위타입(부모)으로 부터 기능 상속을 첫번째 단계에서 확인했다 : Animal 의 새 인스턴스 생성
다음 단계 : 하위타입(자식)의 프로토타입 적용 - 이 경우 Bird 는 Animal의 인스턴스가 된다.
객체생성에서 프로토타입은 레시피와 같다는 것을 기억하라. 여기서 Bird 레시피는 Animal 의 모든 재료를 포함한다.
duckt은 Animal의 eat 메소드를 비롯한 모든 프로퍼티를 상속 받았다.
상속받은 constructor 프로퍼티 초기화
어떤 객체가 다른 객체로 부터 프로토타입을 상속 받을때, 상위타입의 constructor 프로퍼티도 상속 받게 된다.
예제를 살펴보자 :
하지만 duck을 비롯한 모든 Bird의 인스턴스들은 Animal이 아니라 Bird로 부터 생성되었다고 보여줘야한다.
이렇게 하기 위해, Bird의 constructor 프로퍼티를 직접 Bird 객체에 셋팅할 수 있다.
상속 후 메소드 추가
상위타입 생성자 함수로 부터 프로토타입 객체를 상속받는 생성자 함수도 자체 메소드를 가질 수 있다.
예제로, Bird는 Animal로 부터 프로토타입을 상속받는 생성자다.
이제 Animal에서 상속받은 것 외에, Bird 객체에 고유한 기능인 fly() 함수를 추가하려 한다.
함수는 다른 생성자 함수와 동일하게 Bird의 프로토타입으로 추가된다.
이제 Bird의 인스턴스는 eat()과 fly() 메소드를 모두 가진다 :
상속된 메소드 오버라이딩
이전 예제에서, 한 객체가 다른 객체로부터 프로토타입 객체 복제를 통해 기능(메소드)를 상속할 수 있다는 것을 배웠다.
이후 ChildObject는 프로토타입 체이닝을 통해 자체 메소드를 갖는다.
이는 상속된 메소드를 오버라이딩 하는것을 가능하게 한다. 위와 같이 ChildObject.prototype 를 이용해 동일한 이름으로 추가하면 된다.
Bird 객체가 Animal 에서 상속받은 eat() 메소드를 오버라이딩 하는 과정을 살펴 보자 :
만약 let duck = new Bird(); 객체를 만들고 duck.eat() 을 호출 한다면,
자바스크립트는 duck의 프로토타입 체인에서 그 메소드를 찾게 된다 :
1. duck => eat()이 정의되어 있는가? 아니오.
2. Bird => eat()이 정의되어 있는가? 네. 탐색을 멈추고 실행.
3. Animal 또한 eat()을 가지고 있지만, 자바스크립트는 이 곳에 도달하기 전에 탐색을 멈춘다.
4. Object => 마찬가지. 이 곳에 도달하기 전에 탐색을 멈춘다.
믹스인Mixin 활용 : 연관되지 않은 객체간 공통 기능 정의
지금까지 살펴보았듯이, 기능은 상속을 통해 공유 된다. 하지만 상속이 최선이 아닌 경우가 몇가지 존재한다.
상속은 새Bird와 비행기Airplane 처럼 연결되지 않은 객체간에는 사용할 수 없다. 둘 다 날 수 있지만, 새는 비행기와 같은 종이 아니며, 반대의 경우도 마찬가지다.
관련없는 객체들을 위해서는 믹스인minxin을 사용하는 것이 낫다. 믹스인은 다른 객체가 함수들을 사용할 수 있도록 한다.
flyMixin 은 어떤 객체도 받을 수 있으며, 그 객체에 fly 메소드를 정의한다.
새와 비행기 모두 flyMixin 으로 전달되었고, 각 객체에 fly 함수가 정의되었다. 이제 새와 비행기 모두 날 수 있다 :
믹스인이 어떻게 fly 메소드를 서로 관련없는 새와 비행기 객체에서 재사용될 수 있었는지 살펴보라.
클로저closure 사용 : 외부의 수정으로 부터 객체 내부 속성 보호
이전 예제에서, bird 는 이름(name)을 가지고 있다. 이것은 bird 외부에서 접근, 수정할 수 있기 때문에 public으로 간주된다.
그러므로, 코드 어느 부분에서도 bird의 이름을 쉽게 다른 것으로 바꿀 수 있다.
비밀번호나 은행계좌 같은 것을 코드에서 바꿀 수 있다면 이는 많은 문제점을 야기할 것이다.
속성을 private로 만드는 가장 간단한 방법은 생성자 함수 내부에서 변수를 만드는 것이다. 그 변수는 함수 내부에서만 접근할 수 있다.
이 방법을 이용하면, 프로퍼티로 접근 및 수정 또한 생성자 함수 내부의 메소드로만 가능하게 만든다.
여기서 getHachedEggCount 는 privileged 메소드다. private 변수인 hatchedEgg 에 접근했기 때문이다.
이것이 가능한 이유는 hatchedEgg 가 getHachedEggCount 와 동일한 context(실행환경)에 정의되어서다.
자바스크립트에서 함수는 항상 생성된 컨텍스트에 접근할 수 있다. 이를 클로저(closure)라 한다.
즉시 실행 함수 (IIFE - Immediately Invoked Function Expression)
함수 작성과 동시에 실행하게 만들어 주는 자바스크립트 패턴 :
함수에 이름이 없고 변수에 저장되지 않는 다는 점을 주목하라. 함수식 뒤에 붙어있는 괄호 () 가 즉시 실행하게 해준다.
이런 패턴을 즉시 실행 함수(Immediately Invoked Function Expression) 또는 IIFE 라 부른다.
즉시 실행 함수(IIFE)를 이용한 모듈 생성
IIFE 는 단일 객체 또는 모듈에 관련 함수 그룹을 만드는데 자주 사용된다.
예제를 보자. 이전 예제에서 2개의 믹스인을 정의했다.
이제 2개의 믹스인을 하나의 모듈로 만들 수 있다 :
즉시 실행 함수가 객체를 반환하는것에 주목하라. 이 반환된 객체는 모든 믹스인 기능을 motionModule 객체의 프로퍼티로 포함시킨다.
모듈 패턴의 장점은 모든 motion 기능을 하나의 객체로 묶고 이를 다른 코드에서 활용한다는 점이다. 예제를 보자.
정리하며
자바스크립트를 공부할때 큰 좌절을 안겨주는 첫번째 벽이 객체지향 프로그래밍이었다. 위 내용은 출처에 나와있는 개념을 번역한 것이다. 기회가 된다면 꼭 방문해서 예제를 풀어보도록 하자. 이해하는데 굉장히 큰 도움이 될 것이다.
'javascript > etc.' 카테고리의 다른 글
Javascript로 CSS 제어하기(2) - CSSOM : CSS Object Model (2) | 2019.01.10 |
---|---|
Javascript로 CSS 제어하기(1) - CSSOM : CSS Object Model (2) | 2018.12.19 |
console.log : 우리가 모르는 유용한 기능들 (0) | 2018.07.31 |
Node js로 webp 변환기 (webp converter) 만들기 (0) | 2018.07.26 |
DataTables 한글 - language 옵션 설정 (8) | 2018.01.09 |
- Total
- Today
- Yesterday