: OOP의 기본 컨셉은 프로그램 내에서 표현하고자 하는 실 세계(real world)의 일들을 객체를 사용해서 모델링 하고, 객체를 사용하지 않으면 불가능 혹은 무지 어려웠을 일들을 쉽게 처리하는 방법을 제공한다는 것입니다.(출처 : MDN)
위는 MDN 에서 가지고온 간단한 정의이다. 사실 정의가 나같은 초보자들에게는 확 와닿지는 않는다. 그래서 내가 이해한 OOP란, 공장이다. 코드 스테이츠에서도 자동차 공장으로 설명해주고있다. 자동차 공장이라는 설명은 큰 이해를 도왔다.
왜 공장인가? 공장은 여러가지 부품들을 가지고 있고, 어떻게 만드는지 방법을 알고있다. 즉, 청사진을 가지고 있고, 그에 맞는 부품들을 가지고 있다. 이렇게 청사진과 부품이 있다면, 청사진 형식에 맞게 필요한 부품만을 사용하면 새로운 자동차 한대를 만들 수 있다. 여기서 청사진은 Class(템플릿)을 의미하고, 부품은 property 와 method를 의미한다.
: 어떠한 것(ex 자동차,동물)은 여러가지 속성과 행동으로 이루어져 있는데, 이것을 하나의 객체로 묶어서 표현하는 것을 캡슐화이다.
//캡슐화 하기전 데이터들. let pet = cat; let colour = ginger; let name = Mimi; function whatIsThisAnnimal(pet,colour,name){ return `this is my ${pet} called ${name}. she is a ${colour}${pet}` } whatIsThisAnnimal(pet,colour,name); // this is my cat called Mimi. she is a ginger cat.
//캡슐화 let myPet = { pet : 'cat', colour : 'ginger', name : 'Mimi', whatIsThisAnnimal : function(){ return `this is my ${this.pet} called ${this.name}. she is a ${this.colour} ${this.pet}` } } myPet.whatIsThisAnnimal(); // this is my cat called Mimi. she is a ginger cat.
위에 코드를 보면 알듯이, 캡슐화는 모든 정보를 pet 이라는 객체에 담은것이다.
상속이란, 우리가 부모님의 유전자를 상속받는거라고 보면 이해하기가 쉽다. 만약 부모님이 curly hair를 가지고 있다면 나도 가질것이고, 키가 크다면 나도 클것이다. 이렇게 부모라는 Class가 가지고 있는 모든 속성과 메소드를 자식인 Instance가 그 속성과 메소드들을 상속받게되는것이다.(부모는 자식의 속성과 메소드를 사용할 수 없다.)
// Person이라는 부모 class 생성 class Person { constructor(name, country){ this.name = name; this.country = country; } greeting(){ return `Hello, I'm ${this.name} from ${this.country}` } } // 새로운 자식 instance생성 let donghwan = new Person('Donghwan','Korea'); donghwan.greeting(); // Hello, I'm Donghwan from Korea
이렇게 부모를 생성 한 후에 donghwan이라는 자식인 instance를 생성하면 부모가 가지고있던 모든 메소드와 프로퍼티를 사용할 수 있다. 이것이 바로 상속이다.
위의 간단한 상속에서 더 나아가 자식들만의 특별한 메소드나 특성을 추가할 수있다. 자식들은 부모의것을 다 사용할 수 있고, 더 나아가 다른 특성을 가질 수 도 있다.
// brother 라는 새로운 클래스를 생성하고 // extends로 Person의 메소드와 프로퍼티를 상속(extend(확장))한다 class Brother extends Person{ constructor(name, country, height, favFood){ //super로 Pesron의 프로퍼티를 상속 super(name, country); this.height = height; this.favFood = favFood; } //Person의 메소드에서 favFood를 추가 greeting(){ return `Hello, I'm ${this.name} from ${this.country} and also I like ${this.favFood}` } //Brother class만의 메소드 추가 howTall(){ return `I'm ${this.height}cm` } } let chris = new Brother('Chris','Korea',184, 'Chocolate'); chris.greeting() // Hello, I'm Sam from Korea and also I like Chocolate sam.howTall() // I'm 184cm
abstraction은 추상화라는 뜻을 가지고 있고, 우리나라에서도 추상화한다고 말을한다. 그러나 abstraction은 추출이라는 뜻도 가지고있는데 객체지향에서는 추상이라는 개념보다는 '추출'이라는 의미가 더 정확할 것 같은 생각이 든다.
(초보 개발자가 나름의 생각을 적은것이기 때문에 잘 못된 정보를 전달할 수 있는거니, 그냥 초보는 이렇게도 생각하는구나 라고 받아드리시면 될것 같다.)
추출에 더 가깝다고 생각한 이유.
추출은 어떤것에서 물건이나 요소같은 것을 뽑아내는것이다.
아래는 OOP에서 추상화를 정의 내린글이다.
위의 글을 읽었을때, 결국 내가 원하는 것들만 뽑아 내는것이다. 그렇기에 나는 추출에 좀 더 가깝지 않을까라는 생각을 했다.(물론 추상화라는것이 더 맞을 수 있다.)
(위의 그림은 코드 스테이츠에서 가지고 온것)
핸드폰을 Abstraction화 했다는말은 우리가 핸드폰을 사용하기위해서 필요한 부분들만 UI로 만들었다는 의미인것 같다.
: 여러 객체 타입에 같은 기능을 정의할 수 있는것
위의 4가지 장점
encapsulation
코드의 복잡성을 줄일 수 있고, 재사용가능성을 증가시켜준다.
inheritance
코드의 양을 줄여줄 수 있다.(상속받은 코드를 다시 적을 필요없다.)
abstraction
코드의 복잡성을 줄여주고, 변화의 영향을 분리시켜준다.
polymorphism
보기 좋지않은 switch문을 리펙토링할 수 있다.
: 우선 프로토타입이라는 사전적 용어의 의미는 '원형'이다.
JavaScript는 흔히 프로토타입 기반 언어(prototype-based language)라 불립니다. 모든 객체들이 메소드와 속성들을 상속 받기 위한 템플릿으로써 프로토타입 객체(prototype object)를 가진다는 의미입니다.
프로토타입 객체도 또 다시 상위 프로토타입 객체로부터 메소드와 속성을 상속 받을 수도 있고 그 상위 프로토타입 객체도 마찬가지입니다. 이를 프로토타입 체인(prototype chain)이라 부르며 다른 객체에 정의된 메소드와 속성을 한 객체에서 사용할 수 있도록 하는 근간입니다.
정확히 말하자면 상속되는 속성과 메소드들은 각 객체가 아니라 객체의 생성자의 prototype이라는 속성에 정의되어 있습니다.(출처: MDN)
prototype은 복사를 하는것이 아니라 상위 부모객체와 연결 시켜주어 인스턴스가 메소드나 프로퍼티를 가지고 있지 않다면 특정 메소드와 프로퍼티를 찾기위해서 상위 객체로 접근하게 해준다.
: __proto__ 는 우리가 만든 class나 instance의 원형의 Prototype이 무엇인지를 알려주고, 그 원형의 method와 property를 알려준다.
subObj.__proto__ = superObj;
를 실행 하므로써
위의 사진처럼 subObj의 원형은 superObj 가 되었다.
따라서 subObj 는 subVal이라는 프러퍼티만 가지고 있지만, subObj.superVal 를 실행하면 원형의 프러퍼티를 가지고 올 수 있다.
정리하자면,
우리가 class Person을 만들면, Person은 prototype
와 __proto__
라는 두개의 프러퍼티를 자동적으로 가지게 된다.
__proto__
는 class Person에 원형을 가르키게 되고( = 자신을 만든어낸 원형 ),
prototype
은 class Person을 원형으로 만들어진 객체를 말한다.
__proto__보다 더 좋은 방법이라고 말할 수 있는것이 있다.
바로 Object.create() 메소드이다.
이 메소드를 사용하여 다른 객체를 상속받는것을 코드로 구현해 보았다.
let superObj = {superVal : 'super'}; // superObj를 원형으로 갖는 subObj 객체 생성 let subObj = Object.create(superObj) subObj.subVal : 'sub'; //이렇게 해주면 위에서 __proto__와 같은 결과를 가지게 된다.
: super 키워드는 부모 오브젝트의 함수를 호출할 때 사용한다.