-
[JavaScript] 객체 지향 프로그래밍이란 무엇인가오로지 개발/JavaScript 2020. 9. 9. 17:43
Object Oriented Programming
자바를 배울 때부터 항상 OOP에 대한 개념을 이해하기 어려웠다.
면접 때 나온다고 하니까 무작정 외우기도 했었고..
하지만 오늘 글로 정리를 하니 퍼즐이 맞춰지는 듯한 기분이 들었다.
일단 기본적으로 가져가야 할 것은 OOP는 개념, 하나의 철학이라는 것.
따라서 개념과 그 개념을 적용하고 응용하는 상황을 구분해서 생각할 필요가 있는 것 같다.
Object 객체를 그 원래의 의미로 사물 혹은 물건, OOP란 사물을 생산하는 방식이라고 생각하기로 했다.
OOP에는 클래스 기반 언어와 프로토타입 기반 언어가 있다.
클래스 기반 언어에는 대표적인 C++, java, dart 등이 있고, 프로토타입 기반 언어로는 자바스크립트가 있다.
로는 Java, C++, dart... 등, 프로토타입 기반 언어로는 자바스크립트가 있다.
객체 생성 방법
자바스크립트에는 다른 언어에서 사용하는 동일한 개념의 class가 존재하지 않으나 객체를 생성하는 다른 방법들이 있다.
(ES6이후부터는 class 키워드가 등장하지만 그 내부는 prototype으로 구성되어있다)1. {} Object literal 방식
함수를 이용한다.
let MyStatus = { happiness: 5, stress: function() { this.happiness--; if (this.happiness < 0) { return '폭발할 위험이 있음'; } }, drinkBeer: function () { this.happiness++; return '기분이 좋아짐'; } };
장점: 간단하게 작성할 수 있다.
단점: 외부에서 내부 속성에 접근할 수 있기 때문에 객체의 기능을 임의로 조작할 수 있다.단점을 이해하기 위해 객체 리터럴로 생성된 MyStatus의 속성으로 happiness, stress, drinkBeer 세 가지가 있다고 가정해보자.
MyStatus.stress(); MyStatus.drinkBeer();
위의 두 메소드를 실행하면 내 현재의 행복지수가 내려가거나 올라가게 되고,
MyStatus.happiness; 로 내 현재 기분까지 확인할 수 있다.
내 기분의 모든 속성에 접근할 수 있기 때문이다.그런데 만약 다른 이가 메소드 사용 없이 내 기분을 마음대로 조작하게 된다면 어떻게 될까.
myStatus.happiness = -1000;
내 기분(?)의 모든 속성에 접근할 수 있기 때문에 내 기분은 심하게 기뻤다 슬펐다(?) 하게 될 것이다.
누군가가 내 기분을 마음대로 조작하지 않았으면 좋겠다(...)
의도적인 혹은 실수로 인한 내 기분 조작을 막고 싶다면 어떻게 할 수 있을까?
Encapsulation — 캡슐화
캡슐화를 통해 특정 속성을 외부에 노출시키지 않고 내부에서만 조작하는 방법
IIFE 즉시 실행 함수로 객체를 반환하여 scope 사용하기
즉시 실행 함수에 노출시키지 않을 데이터를 변수에 담아 scope로 묶어놓고 반환되는 객체 메소드에서만 접근할 수 있도록 하는 방법
let myStatus = (function () { let happiness = 0; return { stress: function() { this.happiness--; if (this.happiness < 0) { return '폭발할 위험이 있음'; } }, drinkBeer: function () { this.happiness++; return '기분이 좋아짐'; } } })();
위의 예시에서 MyStatus의 속성을 확인해보면
stress와 drinkBeer 두 메소드 뿐이지만 함수가 선언되는 시점에 스코프가 생성되므로 클로저를 사용해 함수의 스코프 내에 접근할 수 있다
따라서 MyStatus의 메소드stress와 drinkBeer를 사용하게 되면 상위 스코프의 변수인 happiness에 접근해 조절할 수 있다.하지만 즉시 실행 함수가 반환하는 객체에 MyStatus.happiness는 포함되지 않았기 때문에 외부에 노출되지 않아 객체 리터럴 방식에서처럼 임의 조작이 불가능하게 된다.
그런데 만약 내 기분 말고 다른 사람들의 기분까지 관리해야 하는 경우가 생긴다면..?
복붙이 방법일까..
2. new 생성자 함수
new 연산자는 사용자 정의 객체 타입 또는 내장 객체 타입의 인스턴스를 생성한다.
다시 말해 생성자 함수를 정의하고 new 연산자와 함께 생성자 함수를 호출해서 생성한 객체를 인스턴스라 부른다.
생성자 함수의 자식이라고 이해해도 된다.
Inheritance — 상속
캡슐화 없이 생성자 함수 사용하여 대량 생산하기
function myStatus() { this.happiness = 0; myStatus.prototype.stress = function() { this.happiness--; if (this.happiness < 0) { return '폭발할 위험이 있음'; } } MyStatus.prototype.drinkBeer = function() { this.happiness++; return '기분이 좋아짐'; } } let myStatus = new MyStatus(); let yourStauts = new MyStatus();
생성자 함수에 재사용할 메소드를 prototype으로 만든 후 new 키워드를 사용하여 원하는 만큼 instance를 생성하면 된다.
생성된 instance는 prototype chain을 통해 생성자 함수의 메소드를 중복된 코드 없이 사용할 수 있게 된다.
하지만 이렇게 되면 객체 리터럴 방식과 마찬가지로
myStatus.happiness = -50; (맥주가 시급..)
위와 같은 코드로 외부에서 내부 값을 변경할 수 있게 되는데
객체 리터럴과 다른 점은 생성자 함수 사용의 경우 대량 생산이 가능하다는 점이다.
만약 사용자에게 충분히 조작 방식에 대해 설명할 수 있거나 외부에서 내부 값 변경을 해야 하는 특별한 상황에서 사용할 수 있다.
3. Object.create()
Object.create는 괄호 속 객체를 prototype으로 하는 객체를 생성한다.
따라서 Object.create(obj.prototype) 은 obj.protytpe을 protytpe으로 하는 객체를 생성하는 것이다.
이때 주의할 것은 객체 생성으로 인해 끊어진 constructor를 재연결해주어야 상위 메소드와 본인이 가지고 있는 메소드를 모두 사용할 수 있다.
Object.create()를 사용한 상속 방법
Status 생성자 함수
function Status() { this.happiness = 0; this.anger = false; } Status.prototype.stress = function() { this.happiness--; if (this.volume === 0) { return '폭발할 위험이 있음'; } } Status.prototype.drinkBeer = function() { this.happiness++; return '기분이 좋아짐'; } Status.prototype.getAnger = function() { if (this.happiness < 0) { this.anger = !this.anger; console.log('폭발했습니다.'); } else { console.log('아직은 괜찮네요. 어서 맥주를 주입하세요.); } }
Status를 __proto__로 가지는 MyStatus 생성자 함수
status와 myStatus의 속성들을 모두 사용할 수 있다.
function MyStatus() { Status.call(this); this.goingOnDate = false; } MyStatus.prototype = Object.create(Status.prototype); //prototype 연결 MyStatus.prototype.constructor = MyStatus; //constructor 재연결 MyStatus.prototype.setGoingOnDate = function() { this.goingOnDate = !this.goingOnDate; } const myStatus = new MyStatus();
Status를 __proto__로 가지는 yourStatus 생성자 함수
status와 yourStatus의 속성들을 모두 사용할 수 있다.
function YourStatus() { Status.call(this); this.askOutOnDate = false; } YourStatus.prototype = Object.create(Status.prototype); //prototype 연결 YourStatus.prototype.constructor = YourStatus; //constructor 재연결 YourStatus.prototype.setAskOutOnDate = function() { this.askOutOnDate = !this.askOutOnDate; } const yourStatus = new YourStatus();
장점: 중복 코드를 줄이며 프로토타입 체인으로 메소드를 상속(위임) 가능하다.
단점: 길어지는 코드와 중요해지는 코드의 위치.
하위 생성자의 경우 메소드 정의 후 상위 생성자와 체인 연결하면 메소드들이 사라진다.ES6 class
위의 단점들을 보완하기 위해 java나 다른 언어에서 익히 보던 class가 등장하게 된다.
ES6부터 등장한 이 친구의 이름은 class이지만 작성법이 달라졌을 뿐 prototype과 크게 다르지 않다.
클래스 선언 후 메소드를 작성해주면 된다.
class Status { constructor() { this.happiness = 0; this.anger = false; } stress = function() { this.happiness--; if (this.happiness < 0) { return '폭발할 위험이 있음'; } } drinkBeer = function () { this.happiness++; return '기분이 좋아짐'; } }
이러면 프로토타입으로 연결을 해줄 필요 없이 아래처럼 상속이 가능하다.
class MyStatus extends Status() { constructor(props) { super(props); this.goOnDate = false; } setGoOnDate = function() { this.goOnDate = !this.goOnDate; } } class YourStatus extends Status() { constructor(props) { super(props); this.askOutOnDate = false; } setAskOutOnDate = function() { this.askOutOnDate = !this.askOutOnDate; } }
'오로지 개발 > JavaScript' 카테고리의 다른 글
[JavaScript] this, call(), apply(), bind() (0) 2020.09.18 [JavaScript] Arrow function expressions 화살표 함수 (0) 2020.09.18 [Data Structure] 스택오버플로우로 알아보는 stack과 recursion (0) 2020.09.09 [Data Structure] Binary Search Tree 이진 탐색 트리 (0) 2020.09.08 자바스크립트 배열 메소드 정리 (0) 2020.08.20