Object-Oriented JavaScript (OOP)

무제·2021년 4월 9일
0
post-thumbnail

OOP가 무엇인가 ?

OOP (객체 지향적 프로그래밍) 는 데이터가 객체 내에 캡슐화되고 구성 요소 부분이 아닌 객체 자체가 운용되는 프로그래밍 방식이다.
📎 MDN

말 그대로 객체 자체로 운용되는 프로그래밍 방식이다. 이와 반대되는 함수형 프로그래밍 방식은 순수하게 함수(순수 함수) 그 자체의 기능만으로 운용되는 프로그래밍 방식이다.
자바스크립트는 기본적으로 아주 객체 지향적인 언어이다. 모든 것이 객체로 이루어졌다고 말해도 과언이 아닐 정도로. 서로가 얽히고 얽혀있으며 그 관계는 프로토타입으로 연결되어 있다. 프로토타입에 대해서는 밑에서 다룰 것 이다. 이제 OOP을 통해 좀 더 쿨하고, 펀하고, 섹시하게 프로그래밍 해보자.

OOP의 4가지 기능

OOP의 근간을 이루고 있는 4대 천왕이 있다.

  • Encapsulation(캡슐화)
  • Abstraction(추상화)
  • Inheritance(상속)
  • Polymorphism(다형성)

Encapsulation(캡슐화)

const Person = function(name, age){
  this.name = name;
  this.age = age;
  this.sayName = function(){
    return `저의 이름은 ${this.name}입니다.`
  }
  this.tellAge = function(){
    return `저의 나이는 ${this.age}입니다.`
  }
}

const ben = new Person('김덕기', '26'); // {name: '김덕기', age :26, sayName, tellAge}

우리가 먹는 💊 캡슐로 제조된 약 안에는 다양한 성분의 약들이 섞여져 있다. 캡슐 안에 있는 성분들은 외부의 오염된 균들로부터 안전하며, 캡슐에 똑같은 양의 약들을 넣어서 같은 캡슐약을 제조하는 데 용이하다.
생성자 함수 Person도 같은 기능을 지닌다. name,age와 같은 정적인 프로퍼티와 정적인 프로퍼티를 사용해 동적인 메소드가 섞여있으며, 오직 생성자 함수안에서만 접근이 가능하기에 외부로부터 안전하며, 한 줄의 코드로 객체들을 생성할 수 있다.

Abstraction(추상화)

const Calculator = function(model){
  this.model = model;
  this.add = function(a, b){
    return a + b;
  }
  this.sub = function(a, b){
    return ...
  }
    ... 나누기, 곱하기, 공학계산 등등..
}

우리는 한 줄의 코드로 간단히 객체를 생성했다. 이 때 우리는 객체 안에서 무슨 일이 벌어지고 있는지는 신경쓰지 않아도 좋다. 예제의 코드는 아주 간단하지만 만약 저 코드들이 계산기의 기능들을 담당하고 있다고 한다면, 더하기, 곱하기, 나누기, 빼기, 공학계산 등등의 복잡한 로직들이 있을 것이다. 그러나 우리는 new를 사용한 한 줄의 코드로 간단히 계산기를 만들고 메소드를 부르기만 하면 된다. 이것이 추상화이다.

Inheritance(상속)

JS는 ES6의 클래스 기능이 있기는 하지만 자바처럼 클래스 기반의 언어가 아닌 프로토타입 기반의 언어이다. 프로토타입을 사용하여 상속의 기능을 사용할 수 있다. 앞서, 상속에 대해 짚고 넘어가자면, new와 함께 생성자 함수의 인스턴스를 만들어낸다. 인스턴스 안에 들어있는 정적인 프로퍼티와 동적인 메소드들은 생성자 함수에서 왔다. 우리는 이것을 '상속했다'라고 표현한다.

const Person = function(name, age){
  this.name = name;
  this.age = age;
}

Person.prototype.sayName = function(){
  return `저의 이름은 ${this.name}입니다.`
}
Person.prototype.tellAge = function(){
  return `저의 나이는 ${this.age}입니다.`
}

const ben = new Person('김덕기', '26'); // {name: '김덕기', age :26}
ben.sayName();  // 저의 이름은 김덕기입니다.
ben.tellAge();  // 저의 나이은 26입니다.

prototype 기능을 통한 상속

위에서 만들었던 Person 생성자 함수를 프로토 타입 기반의 생성자 함수로 진화시켰다.
프로토타입의 기능을 사용해서 만든 인스턴스 ben의 메소드 사용을 보면 프로토타입 기능의 사용에 대해 의문점이 든다. 똑같은 결과와 방법인데 왜 사용하는 걸까 ? 라고 말이다.

그렇지만 다르다.

프로토타입의 기능을 사용하지 않은 인스턴스를 확인하면 프로퍼티와 메소드들이 다 들어있는 것을 볼 수 있다. 이와 다르게 프로토타입의 기능을 사용해서 만든 인스턴스는 프로퍼티만 들어있다. 이 점은 우리에게 메모리 사용에 이점을 준다. 예제는 간단해서 별 차이가 없어 보이지만 만약 엄청난 메소드들이 필요하다면? 상상에 맡기겠다.

또한 매번 인스턴스를 만들 때마다 메소드 전부를 인스턴스에 다 넣어줘야 할까 ? 그렇지 않다. 메모리의 낭비일 뿐이다. 차라리 필요한 기능을 생성자 함수에서 참조한다면 훨씬 메모리 이득을 볼 것이다.

메모리 이득이 바로 프로토타입을 사용하는 이유이다.
프로토타입 객체에 메소드들을 넣어주고 인스턴스들에게 접근하여 인스턴스들의 메소드 참조 및 사용을 허가한다.

참고로 프로토타입에는 정적인 프로퍼티도 넣어줄 수 있다.

Polymorphism(다형성)

const Person = function(name, age){
  this.name = name;
  this.age = age;
}

Person.prototype.sayName = function(){
  return `저의 이름은 ${this.name}입니다.`
}
Person.prototype.tellAge = function(){
  return `저의 나이는 ${this.age}입니다.`
}

const ben = new Person('김덕기', '26'); // {name: '김덕기', age :26}
ben.gender = 'male';

ben; // {name: "김덕기", age: "26", gender: "male"}

상속을 받은 인스턴스들은 인스턴스 자체에도 독립적으로 메소드들을 추가해주거나 프로퍼티를 추가해 줄 수 있다. 그렇기 때문에 상속에 기반해서 다양한 형태로 변화 할 수 있다.

마치며..

얽히고 설킨 관계들을 이해하기엔 시간이 필요한 것 같다.
이상 엄근진한 글을 마친다.

profile
표현할 수 없는 무제공책

0개의 댓글