이번 sprint는 OOP(Object Oriented Programming : 객체 지향 프로그래밍)에 대해 학습한다.
오늘 학습한 것에 대해 아래의 순서대로 TIL을 작성해본다.
(해당 내용은 기술 포스팅이 아닙니다. 개발자 지망생의 TIL입니다. 혹시 포스팅 중에 잘못된 정보가 있다면 꼭 댓글 부탁드립니다!)
- OOP(Object Oriented Programming)가 무엇인가?
- JavaScript에서 Object(class, instance)를 생성하는 여러가지 방법들
- JavaScript에서 Prototype은 무엇이고 왜 사용해야 할까?
태초에 오직 0과 1로 이루어진 Machine Language(기계어)가 있었다. 왠만한 인간의 수준으로는 컴퓨터에게 인간의 의사를 전달하는 것은 꽤.. 많이.. 엄청.. 힘..들었을 것......
그래서 기계어와 1대1로 대치되는 Assembly Language가 등장하게 되었다. 하지만 어셈블리어는 기계(컴퓨터)의 구조, 레지스터의 크기와 개수, 저장된 데이터형의 표현에 따라 각각 다르게 사용해야했기 때문에, 하나의 약속을 구축해 범용적으로 사용하기가 어려웠다.
그래서! 우리가 현재 사용하고 있는 High-level Language(현대의 우리가 알고있는 프로그래밍 언어)가 등장했다. HL Language의 역사에 기반해, 우리는 이를 2가지로 나눌 수 있다.
언어의 패러다임이 2가지가 있다면, 프로그래밍에도 2가지 패러다임이 있다.
절차 지향 프로그래밍과, 객체 지향 프로그래밍.
오늘 학습한 내용은 객체지향 프로그래밍(Object Oriented Programming)이다.
본격적으로 본 내용에 들어가보자.
초창기의 절차 지향적 프로그래밍은, 프로그램 속에 함수와 변수를 전부 몽땅 담아놓고, 이를 어떤 상관관계를 부여해서 어떤 절차로 실행시키는지에 초점을 두는 기법이다.
그러다보니 비슷한 걸 또 만들어야 할 때, 이전에 짰던것과 거의 비슷하게 또 코드를 다시 짜야하니.. 비효율적이지 않을 수 없다. 유지보수도 어렵고, 인간의 사고방식과도 많이 달라 인간이 직관적으로 이해하기가 어려웠다.
두둥. 그 불편함을 해결하려 객체 지향적 프로그래밍이라는 개념이 탄생했다.
(컴퓨터 자체는 인간으로부터 받은 코드를 절차적으로 읽는다. 사람은 기계가 아니기 때문에, 컴퓨터와 더 잘 상호작용 하기 위해 객체를 지향하는 프로그래밍을 한다. 그러니 절차 지향과 객체 지향은 상반되는 것이 아니다. 엄밀히 말하면 공존하는 것이다.
우리가 객체 지향 프로그래밍을 해도, 컴퓨터는 여전히 절차 지향적이다. 그러니 객체 지향 프로그래밍으로'만' 프로그래밍을 한다기 보다는, 두 가지가 공존하는 프로그래밍을 하는 것이라고 할 수 있다.)
그래서 객체 지향적 프로그래밍은,
프로그램을 여러개의 청사진(공통적으로 필요한 property나 method)을 객체로 쪼개어,
그 객체들을 이용해 원하는 instance를 찍어내서 이용하는 패러다임이다.
OOP의 기본적인 컨셉이 있다.
Encapsulation(캡슐화) : 객체에 method와 data가 하나의 unit으로 묶여있어, 임의의 data 조작에 노출되지 않는다. 또한 객체가 각각의 unit으로 나뉘어있어 unit간의 상호의존성이 매우 낮다.
Inheritance(상속) : bass class로부터 파생된 class는 bass의 method를 그대로 상속받아 사용할 수 있다.
Abstraction(추상화) : 그러니 class는 하나의 카테고리에 대한 설계인데, 이 카테고리에 대한 여러 instance들을 잘 찍어내려면, 필요한 특성만을 잘 추려낸 것이어야 한다. 이 작업을 추상화라고 한다.
Polymorphism(다형성) : 다형성은 상속개념과 비슷한데, 부모 class로부터 상속받은 각각의 instance는 같은 형태의 다양한 특성을 가질 수 있다.
하나의 함수에 property와 metod를 지정한다.
let Fruits = function(skin) { // bass(class)의 이름은 대문자로 표기하는 것이 국룰.
let instance = {};
instance.skin = skin;
instance.bite = function() {
return "yummy!"
};
return instance;
};
let apple = Fruits('thin and tough');
console.log(apple); // --> {skin: "thin and tough"}
bass함수와 method함수를 각각 만들어 합친다.
이 방법은 method함수가 변수에 담겨 instance를 생성할 때마다 그 주소만을 참조해오기 때문에,
매번 instance에 method를 받아오는 1번의 functional한 방법보다 메모리 효율이 좋다.
let extend = function(to, from) { //bass와 메소드를 합체해주는 함수
for (let key in from) {
to[key] = from[key];
}
};
let methods = {}; // 메소드 객체를 따로!
methods.bite = function() {
return "yummy!";
};
let Fruits = function(skin) { // bass
let instance = {
skin: skin
};
extend(instance, methods) // 합체!
return instance;
};
let banana = Fruits('thick and soft');
banana.bite(); // --> "yummy!"
이 방법은 2번의 functional shared과 비슷하지만, 객체를 합치는 extend함수를 사용하는 대신 Object객체의 prototype을 이용하는 방법이다.
let methods = {};
methods.bite = function() {
return "yummy!";
};
let Fruits = function(skin) {
let instance = Object.create(methods); // 여기 주목!
instance.skin = skin;
return instance;
};
let grape = Fruits('thin and soft')
grape.bite(); // --> "yummy!"
이 방법은 bass의 메소드를 prototype으로 만들어주는 방법이다. (prototype은 다음 chapter에서 다룬다!)
let Fruits = function(skin, pulp, seed) {
this.skin = skin;
this.pulp = pulp;
this.seed = seed;
};
Fruits.prototype.bite = function() {
return "yummy!"
};
let orange = new Fruits('thick', 'soft', 'very small') // 이 방법대는 new라는 키워드를 사용해야한다!
console.log(orange); // --> Fruits {skin: "thick", pulp: "soft", seed: "very small"}
orange.bite(); // --> "yummy!"
먼저 JavaScript의 대표적인 자료형인 Object와 Array의 prototype을 찍어보자.
(constructor는 객체를 생성하고 초기화하는, class를 생성할 때 쓰이는 메소드이다.)
Object의 prototype을 검색하니 해당 class의 속성과 메소드가 전부 나온다.
그러니까, Object는 Javascript안에 내장되어있는 Object라는 객체를 받아왔다는 것을 알 수 있다.
정리하면, prototype은 JS에 내장되어 있는 함수 method이자, 자신의 prototype(근본)이 되는 다른 객체를 가리키는 특별한 method이다.
(더해서, __proto __는 prototype과 똑같지만 객체에 접근하는 객체method이다.)
이번엔 Array의 prototype을 보자.
뭔가 이상하다. Array의 prototype의 __proto __가 Object를 가리키고 있다.
Array.prototype. __proto __ 와 Object.prototype이 완벽하게 일치한다.
그러니 Array의 root는 Object이고, Object 객체를 상속받아 약간의 다형성을 준 class라고 할 수 있겠다.
사용 방법은 앞서 예시들에서 충분히 보여준 것 같다.
생성된 class의 prototype에 method함수를 부여함으로써 class를 설계하고,
어느 class의 prototype에 접근함으로써 어떤 method함수를 가지고 있는지 확인할 수 있다.
이런 prototype의 특성을 이용해 prototype chain이라는 중요한 개념이 있는데, 이는 바로 다음 블로깅에서 자세하게 알아보자!