티스토리 뷰

Javascript

:객체와 객체지향 프로그래밍


이 내용은 러닝 자바스크립트 의 내용을 참고해서 정리된 포스팅 글입니다. 그리고, 본인의 의견을 담아 내용을 더 추가하여 작성했습니다.




| 프로퍼티 나열


  • 어떤 컨테이너의 콘텐츠를 리스트로 나열하면, 배열이 바로 떠오르지만 객체를 생각하는 사람은 많이 없습니다. 객체도 배열과 같은 컨테이너 이며, 요소들의 나열을 지원 합니다.
  • 단, 프로퍼티 나열에서 중요한점은 순서가 보장되지 않는다는 점입니다. 자바스크립트가 순서를 명시적으로 보장하는 것도 아니고, 브라우저나 노드등의 프로그램에서 속도나 효율 향상을 목적으로 언제든 바꿀 수 있다고도 합니다. 

for ...in


아래에서의 hasOwnProperty 는, 상속 된 property가 for .. in에 나타날 위험을 제거 하기 위해 사용합니다. 다른 타입의 객체, 다른 사람이 만든 객체의 프로퍼티를 나열하다 보면 예상치 못한 상황이 생길 수 있으므로 hasOwnProperty를 쓴느것은 매우 중요하다고 합니다.


const obj = {a:1 , b:2 , c:3};

for(let prop in obj){
 if(obj.hasOwnProperty(prop)) continue;
 console.log(`${prop}` : `${obj[prop]}``)
}

> Object.keys

Object.keys 는 객체에서 나열 가능한 문자열 프로퍼티를 배열로 반환합니다. 아래 예제의 결과 값은 for ... in 루프를 썻을때와 같은 결과이고, hasOwnProperty를 체크할 필요는 없습니다. 그리고, 객체의 프로퍼티 키를 배열로 가져와야 할때 편리하게 쓰일수 있습니다.

const obj = {a:1 , b:2 , c:3};

Object.keys(o).forEach(prop => console.log(`${prop}: ${obj[prop]}`));


| 객체지향 프로그래밍
객체지향 프로그래밍 (OOP)는 컴퓨터 과학에서 전통적인 패러다임 입니다. OOP의 기본 아이디어는 단순하고 직관적입니다. 객체는 데이터와 기능을 논리적으로 묶어 놓은 것입니다. OOP는 우리가 사물을 이해하는 자연스러운 방식을 반영하도록 설계되었습니다.
자동차가 객체라면 그 데이터에는 제조사, 모델, 차량번호 등이 있을 것입니다. 그리고 그 기능으로는 가속,변속, 문 열기 등등이 있을것입니다. 또한 OOP는 이러한 사물에 관해 추상적으로(어떤 자동차), 구체적으로(특정 자동차) 생각 할 수 있게 합니다.
본격적으로 시작하기 전에 OOP의 기본용어에 대해 살펴봅시다.
클래스는 어떤 자동차처럼, 추상적이고 범용적인 것입니다. 인스턴스는 특정 자동차 처럼 구체적이고 한정적인 것입니다. 기능은 메서드 라고 불립니다. 클래스에 속하지만 특정 인스턴스에는 묶이지 않는 기능을 클래스 메서드라고 합니다. 시동을 거는 기능은 클래스 메서드라고 할 수 있는 것입니다. 인스턴스를 처음 만들 때는 생성자가 실행되고, 생성자는 객체 인스턴스를 초기화 합니다.
OOP는 클래스를 계층적으로 분류하는 수단도 제공합니다. 예를 들어, 자동차보다 더 범용적인 운송수단 클래스가 있다치면, 운송수단을 자동차의 슈퍼클래스라 부르고 , 자동차를 운송 수단의 서브클래스 라 부르게 됩니다. 서브클래스 역시 서브클래스를 가질 수 있습니다. 예를들어 보트 서브클래스에는 요트, 카누 등의 서브클래스가 있을 수 있습니다.
> 클래스와 인스턴스 생성
ES6 이전에 자바스크립트에서 클래스를 만드는 건 직관적이지도 않고, 무척 번거러운 일이었다고 합니다. ES6에서는 클래스를 만드는 간편한 새 문법을 도입했다고 합니다.

class Car {
 constructor(){
   
}
}

앞의 코드는 새 클래스 Car를 만들고, 인스턴스는 아래와 같이 new 키워드를 통해 만들 수 있습니다.

const car1 = new Car();
const car2 = new Car();

> 클래스는 함수다.
ES6에서 class 키워드를 도입하기 전까지, 클래스를 만든다는 것은 곧 클래스 생성자로 사용할 함수를 만든다는 의미였습니다. class 문법이 훨씬 더 직관적이고 단순하지만 class는 단축 문법일 뿐이며 자바스크립트의 클래스 자체가 바뀐 것은 아닙니다. 따라서 자바스크립트의 클래스 자체를 이해하는 것이 매우 중요하다고 합니다.

#ES5
function ES5Car(make , model){
 this.make = make;
 this.model = model;
 this._userGears = ['P','N','R','D'];
 this._userGear= this.userGears[0];
}

#ES6
class ES6Car {
 constructor(make,model){
   this.make = make;
   this.model = model;
   this.userGears = ['P','N','R','D'];
   this.userGear = this.userGears[0];
}
}

typeof ES5Car 와 typeof ES6Car 역시 결과가 "function"으로 같을 것입니다.
> 프로토타입
자바스크립트에서의 프로토타입에 관해 이해가 필요한데요, 이에 관해서는 http://www.nextree.co.kr/p7323/ 이블로그를 추천해 드립니다.
클래스의 인스턴스에서 사용 할 수 있는 매서드라고 하면 그건 프로토타입 메서드를 말하는 것입니다. 예를 들어 Car의 인스턴스에서 사용할수 있는 shift라는 메서드는 프로토타입 메서드입니다. 프로토타입 메서드는 Car.prototype.shift 처럼 표기하기도 합니다. Array.forEach를 Array.prototype.forEach라고 쓰는것과 마찬가지인 개념입니다. 많은 배열들에서 forEach 메서드를 쓸수 있는것 역시 상위 Array의 forEach 메서드를 쓰는 것이죠.

모든 함수에는 prototype이라는 특별한 프로퍼티가 있습니다. 일반적인 함수 에서는 프로토타입을 사용할 일이 없지만, 객체 생성자로 동작하는 함수에서는 프로토타입이 대단히 중요합니다.
함수의 prototype은 프로퍼티가 중요해지는 시점은 new 키워드로 새 인스턴스를 만들었을 때 입니다. new 키워드로 만든 새 객체는 생성자의 prototype 프로퍼티에 접근할 수 있습니다.
객체 인스턴스는 생성자의 prototype 프로퍼티를 __proto__ 프로퍼티에 저장합니다.
객체의 프로퍼티나 메서드에 접근하려 할때, 그런 프로퍼티나 메서드가 존재하지 않으면, 자바스크립트는 객체의 프로토타입에서 해당 프로퍼티나 메서드를 찾습니다. 클래스의 인스턴스는 모두 같은 프로토타입을 공유하므로, 프로토타입에 프로퍼티나 메서드가 있다면, 해당 클래스의 인스턴스는 모두 그 프로퍼티나 메서드에 접근할 수 있습니다.
> 정적 메서드 (static method)
메서드에는 인스턴스 메서드 외에도, 정적메서드 ( class method , static method) 가 있습니다. 이 메서드는 특정 인스턴스에 적용되지 않습니다.정적 메서드에서 this는 인스턴스가 아니라 클래스 자체에 묶입니다.
정적 메서드는 클래스에 관련되지만 인스턴스와는 관련이 없는 범용적인 작업에 사용됩니다. 예제로 자동차 식별 번호 (VIN)을 붙이는 메서드를 생각해 봅시다. 개별 자동차가 자신만의 VIN을 붙이는것은 불가능하니, 이러한 상황에서는 정적 메서드를 사용하는 것이라고 합니다. 그리고 이러한 정적 메서드는 여러 인스턴스를 대상으로 하는 작업에도 종종 쓰인다고 합니다.

class Car {
 constructor(){
   this.vin = Car.getNextVin();
}
 
 static getNextVin(){
   return Car.nextVin++;
}
 ...
 
 static areSame(car1,car2){
   return car1.vin === car2.vin;
}
}

> 상속
클래스의 인스턴스는 클래스의 기능을 모두 상속합니다. 상속은 한단계로, 끝나지 않습니다. 객체의 프로토 타입에서 메서드를 찾지 못하면, 자바스크립트는 프로토타입의 프로토타입을 검색합니다. 프로토타입 체인은 이런식으로 만들어 집니다. 그러다 조건에 맞는 프로토 타입을 찾지 못하면 에러가 납니다.
클래스의 계층 구조를 만들 때 프로토타입 체인을 염두해 도면 효율적인 구조를 만들 수 있습니다. 즉, 프로토 타입 체인에서 적절한 위치에 메서드를 정의하는 것입니다.

class Vehicle {
 constructor(){
   this.passengers = [];
   console.log('vehicle created');
}
 
 addPassenger(p){
   this.passengers.push(p);
}
}

class Car extends Vehicle {
 constructor(){
   super();
   console.log('car created');
}
 deployAirbags(){
   console.log('BWOOSH');
}
}

위의 예제에서, extends 키워드는 Car를 Vehicle의 서브클래스로 만듭니다. super( )는 슈퍼클래스의 생성자를 호출하는 특별한 함수입니다. 서브클래스에서는 이 함수를 반드시 호출해야 합니다. 호출하지 않으면 에러가 일어납니다.
리액트를 사용해서, 프론트개발을 하시는 분이면, Dan Abramov 가 쓴 https://overreacted.io/how-does-react-tell-a-class-from-a-function/ 를 한번 읽어보는 것을 추천드립니다. 리액트팀이 리액트 내에서 어떻게 function 기반 컴포넌트와 class 기반 컴포넌트를 분류하는지, 리액트내에의 new 키워드에 대한설명, 그리고 위에서 말한 상속과 관련된 extends.React.component에 대한 설명 등등 아주 흥미로운 내용들이 많이 있습니다.
> 다형성
다형성이라는 단어는 객체지향 언어에서 여러 슈퍼클래스의 멤버인 인스턴스를 가리키는 말입니다. 대부분의 객체지향 언어에서 다형성은 특별한 경우에 속합니다. 자바스크립트는 느슨한 타입을 사용하고 어디서든 객체를 쓸 수 있으므로, 어떤 면에서는 자바스크립트의 객체는 모두 다형성을 갖고 있다고 할 수 있습니다.
> 객체 프로퍼티 나열 다시보기
ES6 클래스를 설계 의도대로 사용한다면 데이터 프로퍼티는 항상 프로토타입 체인이 아니라 인스턴스에 정의해야 합니다. 하지만 프로퍼티를 프로토타입에 정의하지 못하도록 강제하는 장치는 없으므로 확인하려면 항상 hasOwnProperty를 사용하는 편이 좋습니다.

class Super{
 constructor(){
   this.name = 'Super';
   this.isSuper = true;
}
}

Super.prototype.sneaky = 'not recommended';

class Sub extends Super {
 constructor(){
   super();
   this.name = 'Sub';
   this.isSub = true;
}
}

name, isSuper, isSub 프로퍼티는 모두 프로토타입 체인이 아니라 인스턴스에 정의됐습니다. (슈퍼클래스 생성자에서 선언한 프로퍼티는 서브클래스 인스턴스에도 정의됩니다.
Object.keys 를 사용하면, 프로토타입 체인에 정의된 프로퍼티를 나열하는 문제를 피할 수 있습니다.
| 다중 상속, 믹스인, 인터페이스
일부 객체지향 언어에서는 다중 상속이란 기능을 지원합니다. 이는 클래스가 슈퍼클래스 두개를 가지는 기능입니다. 그러나 다중 상속에는 충돌의 위험이 있다고 합니다. 예를 들어 어떤 클래스에 두개의 슈퍼 클래스가 있고, 두 슈퍼클래스에 모두 greet메서드가 있을때 어떤 메서드를 상속해야 할까?와 같은 문제입니다. 다중 상속 언어가 많은 이유는 이러한 문제를 피하기 위헤서라고 합니다.
다중상속을 지원하지 않는 언어 중에는 그 대신 인터페이스 개념을 도입해서 이런 상황에 대처하는 언어가 많습니다.
자바스크립트는 흥미로운 방식으로 이들을 절충했다고 합니다. 자바스크립트는 프로트타입 체인에서 야러 부모를 검색하지는 않으므로 단일 상속 언어라고 해야 하지만, 어떤 면에서는 다중 상속이나 인터페이스보다 더나은 방법을 제공합니다. (더 못할때도 있습니다.)
자바스크립트가 다중 상속이 필요한 문제에 대한 해답으로 내놓은 개념이 믹스인입니다. 믹스인이란 기능을 필요한 만큼 섞어 놓은 것입니다. 자바스크립트는 느슨한 타입을 사용하고 관대한 언어이므로, 그어떤 기능이라도 언제든, 어떤 객체에든, 추가 할 수 있습니다.아래는 makeInsurable 이라는 보험가입과 관련된 믹스인 예제입니다.

class InsurancePolicy{}
function makeInsurable(o){
 o.addInsurancePolicy = function(p){ this.insurancePolicy = p; }
 o.getInsurancePolicy = function() {return this.insurancePolicy; }
 o.isInsured = function(){return !!this.insurancePolicy; }
}

makeInsurable(Car.prototype);
const car1 = new Car();
car1.addInsurancePolicy(new InsurancePolicy());

위와 같이 해주면, addInsurancePolicy 같은 메서드들은 모두 Car 클래스에 정의된 것 처럼 동작합니다. 개발자의 관점에서는 중요한 두 클래스를 관리 쉽게 만들었습니다. 자동차 회사에서 Car 클래스의 개발과 관리를 담당하고, 보험회사에서 InsurancePolicy 클래스와 makeInsurable 믹스인을 관리하게 됩니다.
| 요약
객체지향 프로그래밍은 대단히 널리 쓰이는 패러다임이며, 그럴만한 이유가 있다고 합니다. 객체지향 프로그래밍을 사용하다보면 자연스레 관리하고, 디버그하고, 수저아기 쉬운 정리되고 캡슐화된 코드를 작성하게 됩니다. 자바스크립트의 OOP 구현은 비판을 자주 받는 편이지만, 자바스크립트의 OOP는 사실 매우 유연하고 강력하다고 합니다. 이로인해, 다른 객체지향 언어에서 하기 어려운 일도 할수 있기 때문입니다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
TAG
more
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함